2025-11-17 14:11:46 +08:00
|
|
|
|
package handler
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"dianshang/internal/model"
|
|
|
|
|
|
"dianshang/internal/service"
|
|
|
|
|
|
"dianshang/pkg/response"
|
|
|
|
|
|
"dianshang/pkg/utils"
|
|
|
|
|
|
"strconv"
|
2025-11-28 15:18:10 +08:00
|
|
|
|
"strings"
|
2025-11-17 14:11:46 +08:00
|
|
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// ProductHandler 浜у搧澶勭悊鍣?
|
|
|
|
|
|
type ProductHandler struct {
|
|
|
|
|
|
productService *service.ProductService
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewProductHandler 鍒涘缓浜у搧澶勭悊鍣?
|
|
|
|
|
|
func NewProductHandler(productService *service.ProductService) *ProductHandler {
|
|
|
|
|
|
return &ProductHandler{
|
|
|
|
|
|
productService: productService,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetProductList 鑾峰彇浜у搧鍒楄〃
|
|
|
|
|
|
func (h *ProductHandler) GetProductList(c *gin.Context) {
|
|
|
|
|
|
page := utils.StringToInt(c.DefaultQuery("page", "1"))
|
|
|
|
|
|
pageSize := utils.StringToInt(c.DefaultQuery("page_size", "20"))
|
2025-11-28 15:18:10 +08:00
|
|
|
|
|
|
|
|
|
|
// 支持 category_ids (逗号分隔)或 category_id
|
|
|
|
|
|
var categoryID uint
|
|
|
|
|
|
if categoryIDsStr := c.Query("category_ids"); categoryIDsStr != "" {
|
|
|
|
|
|
// 解析第一个分类ID
|
|
|
|
|
|
ids := strings.Split(categoryIDsStr, ",")
|
|
|
|
|
|
if len(ids) > 0 {
|
|
|
|
|
|
categoryID = utils.StringToUint(strings.TrimSpace(ids[0]))
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
categoryID = utils.StringToUint(c.Query("category_id"))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-17 14:11:46 +08:00
|
|
|
|
keyword := c.Query("keyword")
|
|
|
|
|
|
minPrice, _ := strconv.ParseFloat(c.Query("min_price"), 64)
|
|
|
|
|
|
maxPrice, _ := strconv.ParseFloat(c.Query("max_price"), 64)
|
|
|
|
|
|
|
2025-11-28 15:18:10 +08:00
|
|
|
|
// 库存筛选参数
|
|
|
|
|
|
var inStock *bool
|
|
|
|
|
|
if inStockStr := c.Query("in_stock"); inStockStr != "" {
|
|
|
|
|
|
if inStockStr == "true" {
|
|
|
|
|
|
trueVal := true
|
|
|
|
|
|
inStock = &trueVal
|
|
|
|
|
|
} else if inStockStr == "false" {
|
|
|
|
|
|
falseVal := false
|
|
|
|
|
|
inStock = &falseVal
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-17 14:11:46 +08:00
|
|
|
|
// 处理排序参数:将前端传递的数字参数转换为后端期望的字符串参数
|
|
|
|
|
|
sortParam := c.Query("sort")
|
|
|
|
|
|
sortTypeParam := c.Query("sortType")
|
|
|
|
|
|
|
|
|
|
|
|
var sort, sortType string
|
|
|
|
|
|
|
|
|
|
|
|
// 如果sort=1,表示按价格排序
|
|
|
|
|
|
if sortParam == "1" {
|
|
|
|
|
|
sort = "price"
|
|
|
|
|
|
// sortType: 0=升序(asc), 1=降序(desc)
|
|
|
|
|
|
if sortTypeParam == "0" {
|
|
|
|
|
|
sortType = "asc"
|
|
|
|
|
|
} else {
|
|
|
|
|
|
sortType = "desc"
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 默认排序或其他排序方式
|
|
|
|
|
|
sort = "default"
|
|
|
|
|
|
sortType = "desc"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-28 15:18:10 +08:00
|
|
|
|
products, pagination, err := h.productService.GetProductList(page, pageSize, categoryID, keyword, minPrice, maxPrice, inStock, sort, sortType)
|
2025-11-17 14:11:46 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Page(c, products, pagination.Total, pagination.Page, pagination.PageSize)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetProductDetail 鑾峰彇浜у搧璇︽儏
|
|
|
|
|
|
func (h *ProductHandler) GetProductDetail(c *gin.Context) {
|
|
|
|
|
|
idStr := c.Param("id")
|
|
|
|
|
|
id := utils.StringToUint(idStr)
|
|
|
|
|
|
if id == 0 {
|
|
|
|
|
|
response.BadRequest(c, "鏃犳晥鐨勪骇鍝両D")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
product, err := h.productService.GetProductDetail(id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR_PRODUCT_NOT_FOUND, "产品不存在")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Success(c, product)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CreateProduct 鍒涘缓浜у搧
|
|
|
|
|
|
func (h *ProductHandler) CreateProduct(c *gin.Context) {
|
|
|
|
|
|
var product model.Product
|
|
|
|
|
|
if err := c.ShouldBindJSON(&product); err != nil {
|
|
|
|
|
|
response.BadRequest(c, "鍙傛暟閿欒: "+err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := h.productService.CreateProduct(&product); err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Success(c, product)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UpdateProduct 鏇存柊浜у搧
|
|
|
|
|
|
func (h *ProductHandler) UpdateProduct(c *gin.Context) {
|
|
|
|
|
|
idStr := c.Param("id")
|
|
|
|
|
|
id := utils.StringToUint(idStr)
|
|
|
|
|
|
if id == 0 {
|
|
|
|
|
|
response.BadRequest(c, "鏃犳晥鐨勪骇鍝両D")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var updates map[string]interface{}
|
|
|
|
|
|
if err := c.ShouldBindJSON(&updates); err != nil {
|
|
|
|
|
|
response.BadRequest(c, "鍙傛暟閿欒: "+err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := h.productService.UpdateProduct(id, updates); err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Success(c, nil)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// DeleteProduct 鍒犻櫎浜у搧
|
|
|
|
|
|
func (h *ProductHandler) DeleteProduct(c *gin.Context) {
|
|
|
|
|
|
idStr := c.Param("id")
|
|
|
|
|
|
id := utils.StringToUint(idStr)
|
|
|
|
|
|
if id == 0 {
|
|
|
|
|
|
response.BadRequest(c, "鏃犳晥鐨勪骇鍝両D")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := h.productService.DeleteProduct(id); err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Success(c, nil)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetCategories 鑾峰彇鍒嗙被鍒楄〃
|
|
|
|
|
|
func (h *ProductHandler) GetCategories(c *gin.Context) {
|
2025-11-28 15:18:10 +08:00
|
|
|
|
// 支持平台筛选参数:platform=web 或 platform=miniprogram
|
|
|
|
|
|
platform := c.Query("platform")
|
|
|
|
|
|
|
|
|
|
|
|
var categories []model.Category
|
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
|
|
if platform != "" {
|
|
|
|
|
|
// 根据平台获取分类
|
|
|
|
|
|
categories, err = h.productService.GetCategoriesByPlatform(platform)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 获取所有分类
|
|
|
|
|
|
categories, err = h.productService.GetCategories()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-17 14:11:46 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Success(c, categories)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CreateCategory 鍒涘缓鍒嗙被
|
|
|
|
|
|
func (h *ProductHandler) CreateCategory(c *gin.Context) {
|
|
|
|
|
|
var category model.Category
|
|
|
|
|
|
if err := c.ShouldBindJSON(&category); err != nil {
|
|
|
|
|
|
response.BadRequest(c, "鍙傛暟閿欒: "+err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := h.productService.CreateCategory(&category); err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Success(c, category)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UpdateCategory 鏇存柊鍒嗙被
|
|
|
|
|
|
func (h *ProductHandler) UpdateCategory(c *gin.Context) {
|
|
|
|
|
|
idStr := c.Param("id")
|
|
|
|
|
|
id := utils.StringToUint(idStr)
|
|
|
|
|
|
if id == 0 {
|
|
|
|
|
|
response.BadRequest(c, "鏃犳晥鐨勫垎绫籌D")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var updates map[string]interface{}
|
|
|
|
|
|
if err := c.ShouldBindJSON(&updates); err != nil {
|
|
|
|
|
|
response.BadRequest(c, "鍙傛暟閿欒: "+err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := h.productService.UpdateCategory(id, updates); err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Success(c, nil)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// DeleteCategory 鍒犻櫎鍒嗙被
|
|
|
|
|
|
func (h *ProductHandler) DeleteCategory(c *gin.Context) {
|
|
|
|
|
|
idStr := c.Param("id")
|
|
|
|
|
|
id := utils.StringToUint(idStr)
|
|
|
|
|
|
if id == 0 {
|
|
|
|
|
|
response.BadRequest(c, "鏃犳晥鐨勫垎绫籌D")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := h.productService.DeleteCategory(id); err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Success(c, nil)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetProductReviews 鑾峰彇浜у搧璇勪环鍒楄〃
|
|
|
|
|
|
func (h *ProductHandler) GetProductReviews(c *gin.Context) {
|
|
|
|
|
|
idStr := c.Param("id")
|
|
|
|
|
|
productID := utils.StringToUint(idStr)
|
|
|
|
|
|
if productID == 0 {
|
|
|
|
|
|
response.BadRequest(c, "鏃犳晥鐨勪骇鍝両D")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
page := utils.StringToInt(c.DefaultQuery("page", "1"))
|
|
|
|
|
|
pageSize := utils.StringToInt(c.DefaultQuery("page_size", "20"))
|
|
|
|
|
|
|
|
|
|
|
|
reviews, pagination, err := h.productService.GetProductReviews(productID, page, pageSize)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Page(c, reviews, pagination.Total, pagination.Page, pagination.PageSize)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CreateReview 鍒涘缓璇勪环
|
|
|
|
|
|
func (h *ProductHandler) CreateReview(c *gin.Context) {
|
|
|
|
|
|
userID, exists := c.Get("user_id")
|
|
|
|
|
|
if !exists {
|
|
|
|
|
|
response.Unauthorized(c)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var review model.ProductReview
|
|
|
|
|
|
if err := c.ShouldBindJSON(&review); err != nil {
|
|
|
|
|
|
response.BadRequest(c, "鍙傛暟閿欒: "+err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := h.productService.CreateReview(userID.(uint), &review); err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Success(c, review)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SearchProducts 鎼滅储浜у搧
|
|
|
|
|
|
func (h *ProductHandler) SearchProducts(c *gin.Context) {
|
|
|
|
|
|
keyword := c.Query("keyword")
|
|
|
|
|
|
if keyword == "" {
|
|
|
|
|
|
response.BadRequest(c, "璇疯緭鍏ユ悳绱㈠叧閿瘝")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
page := utils.StringToInt(c.DefaultQuery("page", "1"))
|
|
|
|
|
|
pageSize := utils.StringToInt(c.DefaultQuery("page_size", "20"))
|
|
|
|
|
|
|
|
|
|
|
|
// 价格筛选(后端以“元”存储,参数按“元”传递)
|
|
|
|
|
|
minPrice := utils.StringToFloat64(c.Query("min_price"))
|
|
|
|
|
|
maxPrice := utils.StringToFloat64(c.Query("max_price"))
|
|
|
|
|
|
|
|
|
|
|
|
// 排序参数(搜索页使用 sort_by / sort_order)
|
|
|
|
|
|
sort := c.DefaultQuery("sort_by", "default")
|
|
|
|
|
|
sortType := c.DefaultQuery("sort_order", "desc")
|
|
|
|
|
|
// 兼容其它命名(若存在)
|
|
|
|
|
|
if s := c.Query("sort"); s != "" {
|
|
|
|
|
|
sort = s
|
|
|
|
|
|
}
|
|
|
|
|
|
if st := c.Query("sortType"); st != "" {
|
|
|
|
|
|
sortType = st
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
products, pagination, err := h.productService.SearchProducts(keyword, page, pageSize, minPrice, maxPrice, sort, sortType)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Page(c, products, pagination.Total, pagination.Page, pagination.PageSize)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetHotProducts 鑾峰彇鐑棬浜у搧
|
|
|
|
|
|
func (h *ProductHandler) GetHotProducts(c *gin.Context) {
|
|
|
|
|
|
limit := utils.StringToInt(c.DefaultQuery("limit", "10"))
|
|
|
|
|
|
|
|
|
|
|
|
products, err := h.productService.GetHotProducts(limit)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Success(c, products)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetRecommendProducts 获取推荐产品
|
|
|
|
|
|
func (h *ProductHandler) GetRecommendProducts(c *gin.Context) {
|
|
|
|
|
|
limit := utils.StringToInt(c.DefaultQuery("limit", "10"))
|
|
|
|
|
|
|
|
|
|
|
|
products, err := h.productService.GetRecommendProducts(limit)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Success(c, products)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetProductSKUs 获取产品SKU列表
|
|
|
|
|
|
func (h *ProductHandler) GetProductSKUs(c *gin.Context) {
|
|
|
|
|
|
idStr := c.Param("id")
|
|
|
|
|
|
productID := utils.StringToUint(idStr)
|
|
|
|
|
|
if productID == 0 {
|
|
|
|
|
|
response.BadRequest(c, "无效的产品ID")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
skus, err := h.productService.GetProductSKUs(productID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Success(c, skus)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetProductTags 获取产品标签列表
|
|
|
|
|
|
func (h *ProductHandler) GetProductTags(c *gin.Context) {
|
|
|
|
|
|
tags, err := h.productService.GetProductTags()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Success(c, tags)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetStores 获取店铺列表
|
|
|
|
|
|
func (h *ProductHandler) GetStores(c *gin.Context) {
|
|
|
|
|
|
stores, err := h.productService.GetStores()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Success(c, stores)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetProductReviewCount 获取产品评价统计
|
|
|
|
|
|
func (h *ProductHandler) GetProductReviewCount(c *gin.Context) {
|
|
|
|
|
|
idStr := c.Param("id")
|
|
|
|
|
|
productID := utils.StringToUint(idStr)
|
|
|
|
|
|
if productID == 0 {
|
|
|
|
|
|
response.BadRequest(c, "无效的产品ID")
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
reviewCount, err := h.productService.GetProductReviewCount(productID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
response.Success(c, reviewCount)
|
|
|
|
|
|
}
|