Initial commit: 商品售卖网站
This commit is contained in:
@@ -0,0 +1,356 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"math"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"sale/internal/models"
|
||||
"sale/internal/schemas"
|
||||
"sale/internal/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type CategoryHandler struct{}
|
||||
|
||||
func NewCategoryHandler() *CategoryHandler {
|
||||
return &CategoryHandler{}
|
||||
}
|
||||
|
||||
func (h *CategoryHandler) List(c *gin.Context) {
|
||||
var categories []models.Category
|
||||
utils.DB.Where("parent_id IS NULL").Order("sort_order ASC, id ASC").Find(&categories)
|
||||
for i := range categories {
|
||||
utils.DB.Where("parent_id = ?", categories[i].ID).Order("sort_order ASC, id ASC").Find(&categories[i].Children)
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"data": categories})
|
||||
}
|
||||
|
||||
func (h *CategoryHandler) Create(c *gin.Context) {
|
||||
var req schemas.CreateCategoryRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
category := models.Category{
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
ParentID: req.ParentID,
|
||||
MinAmount: req.MinAmount,
|
||||
MaxAmount: req.MaxAmount,
|
||||
MinQuantity: req.MinQuantity,
|
||||
MaxQuantity: req.MaxQuantity,
|
||||
MinWeight: req.MinWeight,
|
||||
MaxWeight: req.MaxWeight,
|
||||
SortOrder: req.SortOrder,
|
||||
}
|
||||
|
||||
if err := utils.DB.Create(&category).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create category"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{"data": category})
|
||||
}
|
||||
|
||||
func (h *CategoryHandler) Update(c *gin.Context) {
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
var category models.Category
|
||||
if err := utils.DB.First(&category, id).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Category not found"})
|
||||
return
|
||||
}
|
||||
|
||||
var req schemas.UpdateCategoryRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
updates := make(map[string]interface{})
|
||||
if req.Name != nil {
|
||||
updates["name"] = *req.Name
|
||||
}
|
||||
if req.Description != nil {
|
||||
updates["description"] = *req.Description
|
||||
}
|
||||
if req.ParentID != nil {
|
||||
updates["parent_id"] = *req.ParentID
|
||||
}
|
||||
if req.MinAmount != nil {
|
||||
updates["min_amount"] = *req.MinAmount
|
||||
}
|
||||
if req.MaxAmount != nil {
|
||||
updates["max_amount"] = *req.MaxAmount
|
||||
}
|
||||
if req.MinQuantity != nil {
|
||||
updates["min_quantity"] = *req.MinQuantity
|
||||
}
|
||||
if req.MaxQuantity != nil {
|
||||
updates["max_quantity"] = *req.MaxQuantity
|
||||
}
|
||||
if req.MinWeight != nil {
|
||||
updates["min_weight"] = *req.MinWeight
|
||||
}
|
||||
if req.MaxWeight != nil {
|
||||
updates["max_weight"] = *req.MaxWeight
|
||||
}
|
||||
if req.SortOrder != nil {
|
||||
updates["sort_order"] = *req.SortOrder
|
||||
}
|
||||
|
||||
utils.DB.Model(&category).Updates(updates)
|
||||
c.JSON(http.StatusOK, gin.H{"data": category})
|
||||
}
|
||||
|
||||
func (h *CategoryHandler) Delete(c *gin.Context) {
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
if err := utils.DB.Delete(&models.Category{}, id).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete category"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Category deleted successfully"})
|
||||
}
|
||||
|
||||
type ProductHandler struct{}
|
||||
|
||||
func NewProductHandler() *ProductHandler {
|
||||
return &ProductHandler{}
|
||||
}
|
||||
|
||||
func (h *ProductHandler) List(c *gin.Context) {
|
||||
var req schemas.ProductListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
query := utils.DB.Model(&models.Product{}).Where("is_active = ?", true)
|
||||
|
||||
if req.CategoryID != nil {
|
||||
query = query.Joins("JOIN product_categories ON product_categories.product_id = products.id").
|
||||
Where("product_categories.category_id = ?", *req.CategoryID)
|
||||
}
|
||||
|
||||
if req.BrandID != nil {
|
||||
query = query.Where("brand_id = ?", *req.BrandID)
|
||||
}
|
||||
|
||||
if req.Keyword != "" {
|
||||
query = query.Where("name ILIKE ?", "%"+req.Keyword+"%")
|
||||
}
|
||||
|
||||
if req.MinPrice != nil {
|
||||
query = query.Where("price >= ?", *req.MinPrice)
|
||||
}
|
||||
|
||||
if req.MaxPrice != nil {
|
||||
query = query.Where("price <= ?", *req.MaxPrice)
|
||||
}
|
||||
|
||||
var total int64
|
||||
query.Count(&total)
|
||||
|
||||
var products []models.Product
|
||||
offset := (req.Page - 1) * req.PageSize
|
||||
query.Preload("Categories").Preload("CustomFields").Preload("Brand").
|
||||
Order("created_at DESC").
|
||||
Offset(offset).Limit(req.PageSize).
|
||||
Find(&products)
|
||||
|
||||
totalPages := int(math.Ceil(float64(total) / float64(req.PageSize)))
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"data": products,
|
||||
"pagination": gin.H{
|
||||
"page": req.Page,
|
||||
"page_size": req.PageSize,
|
||||
"total": total,
|
||||
"total_pages": totalPages,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (h *ProductHandler) GetByID(c *gin.Context) {
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
var product models.Product
|
||||
if err := utils.DB.Preload("Categories").Preload("CustomFields").First(&product, id).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Product not found"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"data": product})
|
||||
}
|
||||
|
||||
func (h *ProductHandler) AdminList(c *gin.Context) {
|
||||
var products []models.Product
|
||||
utils.DB.Preload("Categories").Preload("Brand").Order("created_at DESC").Find(&products)
|
||||
c.JSON(http.StatusOK, gin.H{"data": products})
|
||||
}
|
||||
|
||||
func (h *ProductHandler) Create(c *gin.Context) {
|
||||
var req schemas.CreateProductRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
product := models.Product{
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
Price: req.Price,
|
||||
MinPurchase: req.MinPurchase,
|
||||
MaxPurchase: req.MaxPurchase,
|
||||
MinWeight: req.MinWeight,
|
||||
MaxWeight: req.MaxWeight,
|
||||
MinAmount: req.MinAmount,
|
||||
MaxAmount: req.MaxAmount,
|
||||
RequireCredit: req.RequireCredit,
|
||||
CreditCost: req.CreditCost,
|
||||
CreditReward: req.CreditReward,
|
||||
Images: req.Images,
|
||||
BrandID: req.BrandID,
|
||||
IsActive: true,
|
||||
}
|
||||
|
||||
if err := utils.DB.Create(&product).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create product"})
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.CategoryIDs) > 0 {
|
||||
var categories []models.Category
|
||||
utils.DB.Where("id IN ?", req.CategoryIDs).Find(&categories)
|
||||
utils.DB.Model(&product).Association("Categories").Replace(categories)
|
||||
}
|
||||
|
||||
if len(req.CustomFields) > 0 {
|
||||
for _, cf := range req.CustomFields {
|
||||
utils.DB.Create(&models.ProductCustomField{
|
||||
ProductID: product.ID,
|
||||
FieldName: cf.FieldName,
|
||||
FieldValue: cf.FieldValue,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
utils.DB.Preload("Categories").Preload("CustomFields").Preload("Brand").First(&product, product.ID)
|
||||
c.JSON(http.StatusCreated, gin.H{"data": product})
|
||||
}
|
||||
|
||||
func (h *ProductHandler) Update(c *gin.Context) {
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
var product models.Product
|
||||
if err := utils.DB.First(&product, id).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Product not found"})
|
||||
return
|
||||
}
|
||||
|
||||
var req schemas.UpdateProductRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
updates := make(map[string]interface{})
|
||||
if req.Name != nil {
|
||||
updates["name"] = *req.Name
|
||||
}
|
||||
if req.Description != nil {
|
||||
updates["description"] = *req.Description
|
||||
}
|
||||
if req.Price != nil {
|
||||
updates["price"] = *req.Price
|
||||
}
|
||||
if req.MinPurchase != nil {
|
||||
updates["min_purchase"] = *req.MinPurchase
|
||||
}
|
||||
if req.MaxPurchase != nil {
|
||||
updates["max_purchase"] = *req.MaxPurchase
|
||||
}
|
||||
if req.MinWeight != nil {
|
||||
updates["min_weight"] = *req.MinWeight
|
||||
}
|
||||
if req.MaxWeight != nil {
|
||||
updates["max_weight"] = *req.MaxWeight
|
||||
}
|
||||
if req.MinAmount != nil {
|
||||
updates["min_amount"] = *req.MinAmount
|
||||
}
|
||||
if req.MaxAmount != nil {
|
||||
updates["max_amount"] = *req.MaxAmount
|
||||
}
|
||||
if req.RequireCredit != nil {
|
||||
updates["require_credit"] = *req.RequireCredit
|
||||
}
|
||||
if req.CreditCost != nil {
|
||||
updates["credit_cost"] = *req.CreditCost
|
||||
}
|
||||
if req.CreditReward != nil {
|
||||
updates["credit_reward"] = *req.CreditReward
|
||||
}
|
||||
if req.Images != nil {
|
||||
updates["images"] = *req.Images
|
||||
}
|
||||
if req.IsActive != nil {
|
||||
updates["is_active"] = *req.IsActive
|
||||
}
|
||||
if req.BrandID != nil {
|
||||
updates["brand_id"] = *req.BrandID
|
||||
}
|
||||
|
||||
utils.DB.Model(&product).Updates(updates)
|
||||
|
||||
if req.CategoryIDs != nil {
|
||||
var categories []models.Category
|
||||
utils.DB.Where("id IN ?", req.CategoryIDs).Find(&categories)
|
||||
utils.DB.Model(&product).Association("Categories").Replace(categories)
|
||||
}
|
||||
|
||||
if req.CustomFields != nil {
|
||||
utils.DB.Where("product_id = ?", product.ID).Delete(&models.ProductCustomField{})
|
||||
for _, cf := range req.CustomFields {
|
||||
utils.DB.Create(&models.ProductCustomField{
|
||||
ProductID: product.ID,
|
||||
FieldName: cf.FieldName,
|
||||
FieldValue: cf.FieldValue,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
utils.DB.Preload("Categories").Preload("CustomFields").Preload("Brand").First(&product, product.ID)
|
||||
c.JSON(http.StatusOK, gin.H{"data": product})
|
||||
}
|
||||
|
||||
func (h *ProductHandler) Delete(c *gin.Context) {
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
if err := utils.DB.Delete(&models.Product{}, id).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete product"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Product deleted successfully"})
|
||||
}
|
||||
|
||||
func (h *ProductHandler) AddCustomField(c *gin.Context) {
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
var product models.Product
|
||||
if err := utils.DB.First(&product, id).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Product not found"})
|
||||
return
|
||||
}
|
||||
|
||||
var req schemas.CustomFieldRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
field := models.ProductCustomField{
|
||||
ProductID: product.ID,
|
||||
FieldName: req.FieldName,
|
||||
FieldValue: req.FieldValue,
|
||||
}
|
||||
|
||||
utils.DB.Create(&field)
|
||||
c.JSON(http.StatusCreated, gin.H{"data": field})
|
||||
}
|
||||
Reference in New Issue
Block a user