1059 lines
27 KiB
Go
1059 lines
27 KiB
Go
package handler
|
||
|
||
import (
|
||
"dianshang/internal/model"
|
||
"dianshang/internal/service"
|
||
"dianshang/pkg/response"
|
||
"dianshang/pkg/utils"
|
||
"fmt"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
// AdminProductHandler 管理后台商品处理器
|
||
type AdminProductHandler struct {
|
||
productService *service.ProductService
|
||
}
|
||
|
||
// NewAdminProductHandler 创建管理后台商品处理器
|
||
func NewAdminProductHandler(productService *service.ProductService) *AdminProductHandler {
|
||
return &AdminProductHandler{
|
||
productService: productService,
|
||
}
|
||
}
|
||
|
||
// GetProductList 获取商品列表
|
||
func (h *AdminProductHandler) GetProductList(c *gin.Context) {
|
||
page := utils.StringToInt(c.DefaultQuery("page", "1"))
|
||
pageSize := utils.StringToInt(c.DefaultQuery("page_size", "10"))
|
||
categoryID := utils.StringToUint(c.Query("category_id"))
|
||
keyword := c.Query("keyword")
|
||
status := c.DefaultQuery("status", "all") // 管理系统默认获取所有状态的商品
|
||
|
||
if page <= 0 {
|
||
page = 1
|
||
}
|
||
if pageSize <= 0 || pageSize > 100 {
|
||
pageSize = 10
|
||
}
|
||
|
||
// 构建查询条件
|
||
minPrice := utils.StringToFloat64(c.Query("min_price"))
|
||
maxPrice := utils.StringToFloat64(c.Query("max_price"))
|
||
sort := c.DefaultQuery("sort", "created_at")
|
||
sortType := c.DefaultQuery("sort_type", "desc")
|
||
|
||
// 获取热门、新品、推荐筛选条件
|
||
isHot := c.Query("is_hot")
|
||
isNew := c.Query("is_new")
|
||
isRecommend := c.Query("is_recommend")
|
||
|
||
products, pagination, err := h.productService.GetProductListForAdmin(page, pageSize, categoryID, keyword, minPrice, maxPrice, sort, sortType, status, isHot, isNew, isRecommend)
|
||
if err != nil {
|
||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||
return
|
||
}
|
||
|
||
response.PageQuery(c, products, pagination.Total, pagination.Page, pagination.PageSize)
|
||
}
|
||
|
||
// GetProductDetail 获取商品详情
|
||
func (h *AdminProductHandler) GetProductDetail(c *gin.Context) {
|
||
idStr := c.Param("id")
|
||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||
if err != nil {
|
||
response.BadRequest(c, "无效的商品ID")
|
||
return
|
||
}
|
||
|
||
product, err := h.productService.GetProductDetail(uint(id))
|
||
if err != nil {
|
||
response.ErrorWithMessage(c, response.ERROR, "商品不存在")
|
||
return
|
||
}
|
||
|
||
response.Success(c, product)
|
||
}
|
||
|
||
// CreateProduct 创建商品
|
||
func (h *AdminProductHandler) CreateProduct(c *gin.Context) {
|
||
var product model.Product
|
||
if err := c.ShouldBindJSON(&product); err != nil {
|
||
response.BadRequest(c, "请求参数错误")
|
||
return
|
||
}
|
||
|
||
// 商品创建时间会自动设置
|
||
|
||
if err := h.productService.CreateProduct(&product); err != nil {
|
||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||
return
|
||
}
|
||
|
||
response.Success(c, product)
|
||
}
|
||
|
||
// UpdateProduct 更新商品
|
||
func (h *AdminProductHandler) UpdateProduct(c *gin.Context) {
|
||
idStr := c.Param("id")
|
||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||
if err != nil {
|
||
response.BadRequest(c, "无效的商品ID")
|
||
return
|
||
}
|
||
|
||
var updates map[string]interface{}
|
||
if err := c.ShouldBindJSON(&updates); err != nil {
|
||
response.BadRequest(c, "请求参数错误")
|
||
return
|
||
}
|
||
|
||
// 商品更新时间会自动设置
|
||
|
||
if err := h.productService.UpdateProduct(uint(id), updates); err != nil {
|
||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||
return
|
||
}
|
||
|
||
response.Success(c, nil)
|
||
}
|
||
|
||
// DeleteProduct 删除商品
|
||
func (h *AdminProductHandler) DeleteProduct(c *gin.Context) {
|
||
idStr := c.Param("id")
|
||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||
if err != nil {
|
||
response.BadRequest(c, "无效的商品ID")
|
||
return
|
||
}
|
||
|
||
if err := h.productService.DeleteProduct(uint(id)); err != nil {
|
||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||
return
|
||
}
|
||
|
||
response.Success(c, nil)
|
||
}
|
||
|
||
// UpdateStock 更新商品库存
|
||
func (h *AdminProductHandler) UpdateStock(c *gin.Context) {
|
||
idStr := c.Param("id")
|
||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||
if err != nil {
|
||
response.BadRequest(c, "无效的商品ID")
|
||
return
|
||
}
|
||
|
||
var req struct {
|
||
Quantity int `json:"quantity" binding:"required"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "请求参数错误")
|
||
return
|
||
}
|
||
|
||
if err := h.productService.UpdateStock(uint(id), req.Quantity); err != nil {
|
||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||
return
|
||
}
|
||
|
||
response.Success(c, nil)
|
||
}
|
||
|
||
// BatchUpdateStatus 批量更新商品状态
|
||
func (h *AdminProductHandler) BatchUpdateStatus(c *gin.Context) {
|
||
var req struct {
|
||
ProductIDs []uint `json:"product_ids" binding:"required"`
|
||
Status uint8 `json:"status" binding:"required"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "请求参数错误")
|
||
return
|
||
}
|
||
|
||
// 批量更新状态
|
||
for _, productID := range req.ProductIDs {
|
||
updates := map[string]interface{}{
|
||
"status": req.Status,
|
||
}
|
||
if adminID, exists := c.Get("user_id"); exists {
|
||
updates["updated_by"] = adminID.(uint)
|
||
}
|
||
|
||
if err := h.productService.UpdateProduct(productID, updates); err != nil {
|
||
response.ErrorWithMessage(c, response.ERROR, "更新商品状态失败")
|
||
return
|
||
}
|
||
}
|
||
|
||
response.Success(c, nil)
|
||
}
|
||
|
||
// GetCategories 获取分类列表
|
||
func (h *AdminProductHandler) GetCategories(c *gin.Context) {
|
||
categories, err := h.productService.GetCategories()
|
||
if err != nil {
|
||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||
return
|
||
}
|
||
|
||
response.SuccessWithMessage(c, "", categories)
|
||
}
|
||
|
||
// CreateCategory 创建分类
|
||
func (h *AdminProductHandler) CreateCategory(c *gin.Context) {
|
||
var category model.Category
|
||
if err := c.ShouldBindJSON(&category); err != nil {
|
||
response.BadRequest(c, "请求参数错误")
|
||
return
|
||
}
|
||
|
||
if err := h.productService.CreateCategory(&category); err != nil {
|
||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||
return
|
||
}
|
||
|
||
response.Success(c, category)
|
||
}
|
||
|
||
// UpdateCategory 更新分类
|
||
func (h *AdminProductHandler) UpdateCategory(c *gin.Context) {
|
||
idStr := c.Param("id")
|
||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||
if err != nil {
|
||
response.BadRequest(c, "无效的分类ID")
|
||
return
|
||
}
|
||
|
||
var updates map[string]interface{}
|
||
if err := c.ShouldBindJSON(&updates); err != nil {
|
||
response.BadRequest(c, "请求参数错误")
|
||
return
|
||
}
|
||
|
||
if err := h.productService.UpdateCategory(uint(id), updates); err != nil {
|
||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||
return
|
||
}
|
||
|
||
response.Success(c, nil)
|
||
}
|
||
|
||
// DeleteCategory 删除分类
|
||
func (h *AdminProductHandler) DeleteCategory(c *gin.Context) {
|
||
idStr := c.Param("id")
|
||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||
if err != nil {
|
||
response.BadRequest(c, "无效的分类ID")
|
||
return
|
||
}
|
||
|
||
if err := h.productService.DeleteCategory(uint(id)); err != nil {
|
||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||
return
|
||
}
|
||
|
||
response.Success(c, nil)
|
||
}
|
||
|
||
// GetProductSKUs 获取商品SKU列表
|
||
func (h *AdminProductHandler) GetProductSKUs(c *gin.Context) {
|
||
idStr := c.Param("id")
|
||
productID, err := strconv.ParseUint(idStr, 10, 32)
|
||
if err != nil {
|
||
response.BadRequest(c, "无效的商品ID")
|
||
return
|
||
}
|
||
|
||
skus, err := h.productService.GetProductSKUs(uint(productID))
|
||
if err != nil {
|
||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||
return
|
||
}
|
||
|
||
response.Success(c, skus)
|
||
}
|
||
|
||
// GetProductReviews 获取商品评价列表
|
||
func (h *AdminProductHandler) GetProductReviews(c *gin.Context) {
|
||
idStr := c.Param("id")
|
||
productID, err := strconv.ParseUint(idStr, 10, 32)
|
||
if err != nil {
|
||
response.BadRequest(c, "无效的商品ID")
|
||
return
|
||
}
|
||
|
||
page := utils.StringToInt(c.DefaultQuery("page", "1"))
|
||
pageSize := utils.StringToInt(c.DefaultQuery("page_size", "10"))
|
||
|
||
if page <= 0 {
|
||
page = 1
|
||
}
|
||
if pageSize <= 0 || pageSize > 100 {
|
||
pageSize = 10
|
||
}
|
||
|
||
reviews, pagination, err := h.productService.GetProductReviews(uint(productID), page, pageSize)
|
||
if err != nil {
|
||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||
return
|
||
}
|
||
|
||
response.Success(c, gin.H{
|
||
"list": reviews,
|
||
"pagination": pagination,
|
||
})
|
||
}
|
||
|
||
// GetStores 获取店铺列表
|
||
func (h *AdminProductHandler) GetStores(c *gin.Context) {
|
||
stores, err := h.productService.GetStores()
|
||
if err != nil {
|
||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||
return
|
||
}
|
||
|
||
response.Success(c, stores)
|
||
}
|
||
|
||
// BatchUpdateProductStatus 批量更新商品状态
|
||
func (h *AdminProductHandler) BatchUpdateProductStatus(c *gin.Context) {
|
||
var req struct {
|
||
IDs []uint `json:"ids" binding:"required"`
|
||
Status int `json:"status" binding:"required"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(400, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
|
||
if err := h.productService.BatchUpdateProductStatus(req.IDs, req.Status); err != nil {
|
||
c.JSON(500, gin.H{"error": "批量更新商品状态失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{"message": "批量更新商品状态成功"})
|
||
}
|
||
|
||
// BatchUpdateProductPrice 批量更新商品价格
|
||
func (h *AdminProductHandler) BatchUpdateProductPrice(c *gin.Context) {
|
||
var req struct {
|
||
Updates []map[string]interface{} `json:"updates" binding:"required"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(400, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
|
||
if err := h.productService.BatchUpdateProductPrice(req.Updates); err != nil {
|
||
c.JSON(500, gin.H{"error": "批量更新商品价格失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{"message": "批量更新商品价格成功"})
|
||
}
|
||
|
||
// BatchDeleteProducts 批量删除商品
|
||
func (h *AdminProductHandler) BatchDeleteProducts(c *gin.Context) {
|
||
var req struct {
|
||
IDs []uint `json:"ids" binding:"required"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "请求参数错误")
|
||
return
|
||
}
|
||
|
||
if err := h.productService.BatchDeleteProducts(req.IDs); err != nil {
|
||
response.ErrorWithMessage(c, response.ERROR, "批量删除商品失败")
|
||
return
|
||
}
|
||
|
||
response.SuccessWithMessage(c, "批量删除商品成功", nil)
|
||
}
|
||
|
||
// CreateProductSKU 创建商品SKU
|
||
func (h *AdminProductHandler) CreateProductSKU(c *gin.Context) {
|
||
var req struct {
|
||
ProductID uint `json:"product_id" binding:"required"`
|
||
SpecValues string `json:"spec_values" binding:"required"`
|
||
Price float64 `json:"price" binding:"required"`
|
||
OriginalPrice float64 `json:"original_price"` // 划线价
|
||
Stock int `json:"stock" binding:"required"`
|
||
SKUCode string `json:"sku_code" binding:"required"`
|
||
Image string `json:"image"`
|
||
Weight float64 `json:"weight"`
|
||
Volume float64 `json:"volume"`
|
||
Sort int `json:"sort"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(400, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
|
||
// 将SpecValues字符串转换为JSONMap
|
||
specValuesMap := make(model.JSONMap)
|
||
if req.SpecValues != "" {
|
||
// 这里简化处理,实际应该解析JSON字符串
|
||
specValuesMap["spec"] = req.SpecValues
|
||
}
|
||
|
||
sku := &model.ProductSKU{
|
||
ProductID: req.ProductID,
|
||
SpecValues: specValuesMap,
|
||
Price: req.Price,
|
||
OriginalPrice: req.OriginalPrice,
|
||
Stock: req.Stock,
|
||
SKUCode: req.SKUCode,
|
||
Image: req.Image,
|
||
Weight: req.Weight,
|
||
}
|
||
|
||
err := h.productService.CreateProductSKU(sku)
|
||
if err != nil {
|
||
c.JSON(500, gin.H{"error": "创建商品SKU失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{
|
||
"message": "创建商品SKU成功",
|
||
"data": sku,
|
||
})
|
||
}
|
||
|
||
// UpdateProductSKU 更新商品SKU
|
||
func (h *AdminProductHandler) UpdateProductSKU(c *gin.Context) {
|
||
id := c.Param("id")
|
||
skuID, err := strconv.ParseUint(id, 10, 32)
|
||
if err != nil {
|
||
c.JSON(400, gin.H{"error": "无效的SKU ID"})
|
||
return
|
||
}
|
||
|
||
var req struct {
|
||
SpecValues *string `json:"spec_values"`
|
||
Price *float64 `json:"price"`
|
||
OriginalPrice *float64 `json:"original_price"` // 划线价
|
||
Stock *int `json:"stock"`
|
||
SKUCode *string `json:"sku_code"`
|
||
Image *string `json:"image"`
|
||
Weight *float64 `json:"weight"`
|
||
Volume *float64 `json:"volume"`
|
||
Sort *int `json:"sort"`
|
||
Status *int `json:"status"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(400, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
|
||
updates := make(map[string]interface{})
|
||
if req.SpecValues != nil {
|
||
updates["spec_values"] = *req.SpecValues
|
||
}
|
||
if req.Price != nil {
|
||
updates["price"] = *req.Price
|
||
}
|
||
if req.OriginalPrice != nil {
|
||
updates["original_price"] = *req.OriginalPrice
|
||
}
|
||
if req.Stock != nil {
|
||
updates["stock"] = *req.Stock
|
||
}
|
||
if req.SKUCode != nil {
|
||
updates["sku_code"] = *req.SKUCode
|
||
}
|
||
if req.Image != nil {
|
||
updates["image"] = *req.Image
|
||
}
|
||
if req.Weight != nil {
|
||
updates["weight"] = *req.Weight
|
||
}
|
||
|
||
if req.Status != nil {
|
||
updates["status"] = *req.Status
|
||
}
|
||
|
||
if err := h.productService.UpdateProductSKU(uint(skuID), updates); err != nil {
|
||
c.JSON(500, gin.H{"error": "更新商品SKU失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{"message": "更新商品SKU成功"})
|
||
}
|
||
|
||
// DeleteProductSKU 删除商品SKU
|
||
func (h *AdminProductHandler) DeleteProductSKU(c *gin.Context) {
|
||
id := c.Param("id")
|
||
skuID, err := strconv.ParseUint(id, 10, 32)
|
||
if err != nil {
|
||
c.JSON(400, gin.H{"error": "无效的SKU ID"})
|
||
return
|
||
}
|
||
|
||
if err := h.productService.DeleteProductSKU(uint(skuID)); err != nil {
|
||
c.JSON(500, gin.H{"error": "删除商品SKU失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{"message": "删除商品SKU成功"})
|
||
}
|
||
|
||
// GetProductImages 获取商品图片列表
|
||
func (h *AdminProductHandler) GetProductImages(c *gin.Context) {
|
||
id := c.Param("id")
|
||
productID, err := strconv.ParseUint(id, 10, 32)
|
||
if err != nil {
|
||
c.JSON(400, gin.H{"error": "无效的商品ID"})
|
||
return
|
||
}
|
||
|
||
images, err := h.productService.GetProductImages(uint(productID))
|
||
if err != nil {
|
||
c.JSON(500, gin.H{"error": "获取商品图片失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{
|
||
"message": "获取商品图片成功",
|
||
"data": images,
|
||
})
|
||
}
|
||
|
||
// CreateProductImage 创建商品图片
|
||
func (h *AdminProductHandler) CreateProductImage(c *gin.Context) {
|
||
var req struct {
|
||
ProductID uint `json:"product_id" binding:"required"`
|
||
ImageURL string `json:"image_url" binding:"required"`
|
||
Sort int `json:"sort"`
|
||
Type int `json:"type"` // 0:普通图片 1:主图
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(400, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
|
||
productImage := &model.ProductImage{
|
||
ProductID: req.ProductID,
|
||
ImageURL: req.ImageURL,
|
||
Sort: req.Sort,
|
||
}
|
||
|
||
err := h.productService.CreateProductImage(productImage)
|
||
if err != nil {
|
||
c.JSON(500, gin.H{"error": "创建商品图片失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{
|
||
"message": "创建商品图片成功",
|
||
"data": productImage,
|
||
})
|
||
}
|
||
|
||
// UpdateProductImageSort 更新商品图片排序
|
||
func (h *AdminProductHandler) UpdateProductImageSort(c *gin.Context) {
|
||
id := c.Param("id")
|
||
imageID, err := strconv.ParseUint(id, 10, 32)
|
||
if err != nil {
|
||
c.JSON(400, gin.H{"error": "无效的图片ID"})
|
||
return
|
||
}
|
||
|
||
var req struct {
|
||
Sort int `json:"sort" binding:"required"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(400, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
|
||
if err := h.productService.UpdateProductImageSort(uint(imageID), req.Sort); err != nil {
|
||
c.JSON(500, gin.H{"error": "更新图片排序失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{"message": "更新图片排序成功"})
|
||
}
|
||
|
||
// DeleteProductImage 删除商品图片
|
||
func (h *AdminProductHandler) DeleteProductImage(c *gin.Context) {
|
||
id := c.Param("id")
|
||
imageID, err := strconv.ParseUint(id, 10, 32)
|
||
if err != nil {
|
||
c.JSON(400, gin.H{"error": "无效的图片ID"})
|
||
return
|
||
}
|
||
|
||
if err := h.productService.DeleteProductImage(uint(imageID)); err != nil {
|
||
c.JSON(500, gin.H{"error": "删除商品图片失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{"message": "删除商品图片成功"})
|
||
}
|
||
|
||
// CreateProductSpec 创建商品规格
|
||
func (h *AdminProductHandler) CreateProductSpec(c *gin.Context) {
|
||
var req struct {
|
||
ProductID uint `json:"product_id" binding:"required"`
|
||
Name string `json:"name" binding:"required"`
|
||
Values string `json:"values" binding:"required"`
|
||
Sort int `json:"sort"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(400, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
|
||
productSpec := &model.ProductSpec{
|
||
ProductID: req.ProductID,
|
||
Name: req.Name,
|
||
Value: req.Values,
|
||
Sort: req.Sort,
|
||
}
|
||
|
||
err := h.productService.CreateProductSpec(productSpec)
|
||
if err != nil {
|
||
c.JSON(500, gin.H{"error": "创建商品规格失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{
|
||
"message": "创建商品规格成功",
|
||
"data": productSpec,
|
||
})
|
||
}
|
||
|
||
// UpdateProductSpec 更新商品规格
|
||
func (h *AdminProductHandler) UpdateProductSpec(c *gin.Context) {
|
||
id := c.Param("id")
|
||
specID, err := strconv.ParseUint(id, 10, 32)
|
||
if err != nil {
|
||
c.JSON(400, gin.H{"error": "无效的规格ID"})
|
||
return
|
||
}
|
||
|
||
var req struct {
|
||
Name *string `json:"name"`
|
||
Values *string `json:"values"`
|
||
Sort *int `json:"sort"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(400, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
|
||
updates := make(map[string]interface{})
|
||
if req.Name != nil {
|
||
updates["name"] = *req.Name
|
||
}
|
||
if req.Values != nil {
|
||
updates["value"] = *req.Values
|
||
}
|
||
if req.Sort != nil {
|
||
updates["sort"] = *req.Sort
|
||
}
|
||
|
||
if err := h.productService.UpdateProductSpec(uint(specID), updates); err != nil {
|
||
c.JSON(500, gin.H{"error": "更新商品规格失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{"message": "更新商品规格成功"})
|
||
}
|
||
|
||
// DeleteProductSpec 删除商品规格
|
||
func (h *AdminProductHandler) DeleteProductSpec(c *gin.Context) {
|
||
id := c.Param("id")
|
||
specID, err := strconv.ParseUint(id, 10, 32)
|
||
if err != nil {
|
||
c.JSON(400, gin.H{"error": "无效的规格ID"})
|
||
return
|
||
}
|
||
|
||
if err := h.productService.DeleteProductSpec(uint(specID)); err != nil {
|
||
c.JSON(500, gin.H{"error": "删除商品规格失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{"message": "删除商品规格成功"})
|
||
}
|
||
|
||
// GetProductSpecs 获取商品规格列表
|
||
func (h *AdminProductHandler) GetProductSpecs(c *gin.Context) {
|
||
id := c.Param("id")
|
||
productID, err := strconv.ParseUint(id, 10, 32)
|
||
if err != nil {
|
||
c.JSON(400, gin.H{"error": "无效的商品ID"})
|
||
return
|
||
}
|
||
|
||
specs, err := h.productService.GetProductSpecs(uint(productID))
|
||
if err != nil {
|
||
c.JSON(500, gin.H{"error": "获取商品规格失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{
|
||
"message": "获取商品规格成功",
|
||
"data": specs,
|
||
})
|
||
}
|
||
|
||
// CreateProductTag 创建商品标签
|
||
func (h *AdminProductHandler) CreateProductTag(c *gin.Context) {
|
||
var req struct {
|
||
Name string `json:"name" binding:"required"`
|
||
Color string `json:"color"`
|
||
Sort int `json:"sort"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(400, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
|
||
productTag := &model.ProductTag{
|
||
Name: req.Name,
|
||
Color: req.Color,
|
||
Sort: req.Sort,
|
||
}
|
||
|
||
err := h.productService.CreateProductTag(productTag)
|
||
if err != nil {
|
||
c.JSON(500, gin.H{"error": "创建商品标签失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{
|
||
"message": "创建商品标签成功",
|
||
"data": productTag,
|
||
})
|
||
}
|
||
|
||
// UpdateProductTag 更新商品标签
|
||
func (h *AdminProductHandler) UpdateProductTag(c *gin.Context) {
|
||
id := c.Param("id")
|
||
tagID, err := strconv.ParseUint(id, 10, 32)
|
||
if err != nil {
|
||
c.JSON(400, gin.H{"error": "无效的标签ID"})
|
||
return
|
||
}
|
||
|
||
var req struct {
|
||
Name *string `json:"name"`
|
||
Color *string `json:"color"`
|
||
Sort *int `json:"sort"`
|
||
Status *int `json:"status"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(400, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
|
||
updates := make(map[string]interface{})
|
||
if req.Name != nil {
|
||
updates["name"] = *req.Name
|
||
}
|
||
if req.Color != nil {
|
||
updates["color"] = *req.Color
|
||
}
|
||
if req.Sort != nil {
|
||
updates["sort"] = *req.Sort
|
||
}
|
||
if req.Status != nil {
|
||
updates["status"] = *req.Status
|
||
}
|
||
|
||
if err := h.productService.UpdateProductTag(uint(tagID), updates); err != nil {
|
||
c.JSON(500, gin.H{"error": "更新商品标签失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{"message": "更新商品标签成功"})
|
||
}
|
||
|
||
// DeleteProductTag 删除商品标签
|
||
func (h *AdminProductHandler) DeleteProductTag(c *gin.Context) {
|
||
id := c.Param("id")
|
||
tagID, err := strconv.ParseUint(id, 10, 32)
|
||
if err != nil {
|
||
c.JSON(400, gin.H{"error": "无效的标签ID"})
|
||
return
|
||
}
|
||
|
||
if err := h.productService.DeleteProductTag(uint(tagID)); err != nil {
|
||
c.JSON(500, gin.H{"error": "删除商品标签失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{"message": "删除商品标签成功"})
|
||
}
|
||
|
||
// AssignTagsToProduct 为商品分配标签
|
||
func (h *AdminProductHandler) AssignTagsToProduct(c *gin.Context) {
|
||
id := c.Param("id")
|
||
productID, err := strconv.ParseUint(id, 10, 32)
|
||
if err != nil {
|
||
c.JSON(400, gin.H{"error": "无效的商品ID"})
|
||
return
|
||
}
|
||
|
||
var req struct {
|
||
TagIDs []uint `json:"tag_ids" binding:"required"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(400, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
|
||
if err := h.productService.AssignTagsToProduct(uint(productID), req.TagIDs); err != nil {
|
||
c.JSON(500, gin.H{"error": "分配商品标签失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{"message": "分配商品标签成功"})
|
||
}
|
||
|
||
// GetLowStockProducts 获取低库存商品
|
||
func (h *AdminProductHandler) GetLowStockProducts(c *gin.Context) {
|
||
threshold := 10 // 默认阈值
|
||
if t := c.Query("threshold"); t != "" {
|
||
if th, err := strconv.Atoi(t); err == nil {
|
||
threshold = th
|
||
}
|
||
}
|
||
|
||
products, err := h.productService.GetLowStockProducts(threshold)
|
||
if err != nil {
|
||
c.JSON(500, gin.H{"error": "获取低库存商品失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{
|
||
"message": "获取低库存商品成功",
|
||
"data": products,
|
||
})
|
||
}
|
||
|
||
// GetInventoryStatistics 获取库存统计
|
||
func (h *AdminProductHandler) GetInventoryStatistics(c *gin.Context) {
|
||
stats, err := h.productService.GetInventoryStatistics()
|
||
if err != nil {
|
||
c.JSON(500, gin.H{"error": "获取库存统计失败"})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{
|
||
"message": "获取库存统计成功",
|
||
"data": stats,
|
||
})
|
||
}
|
||
|
||
// ExportProducts 导出商品数据
|
||
func (h *AdminProductHandler) ExportProducts(c *gin.Context) {
|
||
// 获取查询条件
|
||
conditions := make(map[string]interface{})
|
||
|
||
if categoryID := c.Query("category_id"); categoryID != "" {
|
||
if id, err := strconv.ParseUint(categoryID, 10, 32); err == nil {
|
||
conditions["category_id"] = uint(id)
|
||
}
|
||
}
|
||
|
||
if status := c.Query("status"); status != "" {
|
||
if s, err := strconv.Atoi(status); err == nil {
|
||
conditions["status"] = s
|
||
}
|
||
}
|
||
|
||
if keyword := c.Query("keyword"); keyword != "" {
|
||
conditions["keyword"] = keyword
|
||
}
|
||
|
||
// 导出商品数据
|
||
products, err := h.productService.ExportProducts(conditions)
|
||
if err != nil {
|
||
c.JSON(500, gin.H{"error": "导出商品数据失败"})
|
||
return
|
||
}
|
||
|
||
// TODO: 这里需要实现将products转换为Excel文件并保存的逻辑
|
||
// 暂时直接返回产品数据
|
||
c.JSON(200, gin.H{
|
||
"message": "导出商品数据成功",
|
||
"data": products,
|
||
})
|
||
}
|
||
|
||
// ImportProducts 导入商品数据
|
||
func (h *AdminProductHandler) ImportProducts(c *gin.Context) {
|
||
file, header, err := c.Request.FormFile("file")
|
||
if err != nil {
|
||
c.JSON(400, gin.H{"error": "获取上传文件失败"})
|
||
return
|
||
}
|
||
defer file.Close()
|
||
|
||
// 检查文件类型
|
||
if !strings.HasSuffix(header.Filename, ".xlsx") && !strings.HasSuffix(header.Filename, ".xls") {
|
||
c.JSON(400, gin.H{"error": "文件格式不支持,请上传Excel文件"})
|
||
return
|
||
}
|
||
|
||
// TODO: 这里需要实现Excel文件解析逻辑,将文件内容解析为[]model.Product
|
||
// 暂时返回错误,提示功能未实现
|
||
c.JSON(500, gin.H{"error": "导入功能暂未实现,需要添加Excel文件解析逻辑"})
|
||
}
|
||
|
||
// SyncProductStock 同步单个商品库存
|
||
func (h *AdminProductHandler) SyncProductStock(c *gin.Context) {
|
||
id := c.Param("id")
|
||
productID, err := strconv.ParseUint(id, 10, 32)
|
||
if err != nil {
|
||
c.JSON(400, gin.H{"error": "无效的商品ID"})
|
||
return
|
||
}
|
||
|
||
if err := h.productService.SyncProductStock(uint(productID)); err != nil {
|
||
c.JSON(500, gin.H{"error": "同步商品库存失败: " + err.Error()})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{"message": "同步商品库存成功"})
|
||
}
|
||
|
||
// SyncAllProductsStock 同步所有商品库存
|
||
func (h *AdminProductHandler) SyncAllProductsStock(c *gin.Context) {
|
||
if err := h.productService.SyncAllProductsStock(); err != nil {
|
||
c.JSON(500, gin.H{"error": "同步所有商品库存失败: " + err.Error()})
|
||
return
|
||
}
|
||
|
||
c.JSON(200, gin.H{"message": "同步所有商品库存成功"})
|
||
}
|
||
|
||
// BatchUpdateOrigPrice 批量更新SPU划线价
|
||
func (h *AdminProductHandler) BatchUpdateOrigPrice(c *gin.Context) {
|
||
var req struct {
|
||
Products []struct {
|
||
ID uint `json:"id" binding:"required"`
|
||
OrigPrice float64 `json:"orig_price" binding:"required"`
|
||
} `json:"products" binding:"required,min=1"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "请求参数错误: "+err.Error())
|
||
return
|
||
}
|
||
|
||
// 批量更新
|
||
successCount := 0
|
||
failedCount := 0
|
||
var errors []string
|
||
|
||
for _, item := range req.Products {
|
||
updates := map[string]interface{}{
|
||
"orig_price": item.OrigPrice,
|
||
}
|
||
|
||
if err := h.productService.UpdateProduct(item.ID, updates); err != nil {
|
||
failedCount++
|
||
errors = append(errors, fmt.Sprintf("商品ID %d 更新失败: %v", item.ID, err))
|
||
} else {
|
||
successCount++
|
||
}
|
||
}
|
||
|
||
response.Success(c, gin.H{
|
||
"message": fmt.Sprintf("批量更新完成,成功 %d 个,失败 %d 个", successCount, failedCount),
|
||
"success_count": successCount,
|
||
"failed_count": failedCount,
|
||
"errors": errors,
|
||
})
|
||
}
|
||
|
||
// BatchUpdateSKUOrigPrice 批量更新SKU划线价
|
||
func (h *AdminProductHandler) BatchUpdateSKUOrigPrice(c *gin.Context) {
|
||
var req struct {
|
||
SKUs []struct {
|
||
ID uint `json:"id" binding:"required"`
|
||
OriginalPrice float64 `json:"original_price" binding:"required"`
|
||
} `json:"skus" binding:"required,min=1"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "请求参数错误: "+err.Error())
|
||
return
|
||
}
|
||
|
||
// 批量更新
|
||
successCount := 0
|
||
failedCount := 0
|
||
var errors []string
|
||
|
||
for _, item := range req.SKUs {
|
||
updates := map[string]interface{}{
|
||
"original_price": item.OriginalPrice,
|
||
}
|
||
|
||
if err := h.productService.UpdateProductSKU(item.ID, updates); err != nil {
|
||
failedCount++
|
||
errors = append(errors, fmt.Sprintf("SKU ID %d 更新失败: %v", item.ID, err))
|
||
} else {
|
||
successCount++
|
||
}
|
||
}
|
||
|
||
response.Success(c, gin.H{
|
||
"message": fmt.Sprintf("批量更新完成,成功 %d 个,失败 %d 个", successCount, failedCount),
|
||
"success_count": successCount,
|
||
"failed_count": failedCount,
|
||
"errors": errors,
|
||
})
|
||
}
|
||
|
||
// UpdateProductOrigPrice 更新单个商品SPU划线价
|
||
func (h *AdminProductHandler) UpdateProductOrigPrice(c *gin.Context) {
|
||
idStr := c.Param("id")
|
||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||
if err != nil {
|
||
response.BadRequest(c, "无效的商品ID")
|
||
return
|
||
}
|
||
|
||
var req struct {
|
||
OrigPrice float64 `json:"orig_price" binding:"required"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, "请求参数错误")
|
||
return
|
||
}
|
||
|
||
updates := map[string]interface{}{
|
||
"orig_price": req.OrigPrice,
|
||
}
|
||
|
||
if err := h.productService.UpdateProduct(uint(id), updates); err != nil {
|
||
response.ErrorWithMessage(c, response.ERROR, "更新划线价失败: "+err.Error())
|
||
return
|
||
}
|
||
|
||
response.SuccessWithMessage(c, "更新划线价成功", nil)
|
||
} |