package handlers import ( "encoding/json" "math" "net/http" "strconv" "sale/internal/models" "sale/internal/schemas" "sale/internal/utils" "github.com/gin-gonic/gin" ) type ShippingTemplateHandler struct{} func NewShippingTemplateHandler() *ShippingTemplateHandler { return &ShippingTemplateHandler{} } // List 获取运费模板列表 func (h *ShippingTemplateHandler) List(c *gin.Context) { var templates []models.ShippingTemplate utils.DB.Order("sort_order ASC, id ASC").Find(&templates) // 解析 provinces JSON 为数组 type TemplateResponse struct { models.ShippingTemplate ProvincesList []string `json:"provinces_list"` } var response []TemplateResponse for _, t := range templates { var provinces []string if t.Provinces != "" { json.Unmarshal([]byte(t.Provinces), &provinces) } response = append(response, TemplateResponse{ ShippingTemplate: t, ProvincesList: provinces, }) } c.JSON(http.StatusOK, gin.H{"data": response}) } // GetByID 获取单个运费模板 func (h *ShippingTemplateHandler) GetByID(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) var template models.ShippingTemplate if err := utils.DB.First(&template, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "运费模板不存在"}) return } var provinces []string if template.Provinces != "" { json.Unmarshal([]byte(template.Provinces), &provinces) } c.JSON(http.StatusOK, gin.H{ "data": gin.H{ "ShippingTemplate": template, "provinces_list": provinces, }, }) } // Create 创建运费模板 func (h *ShippingTemplateHandler) Create(c *gin.Context) { var req schemas.CreateShippingTemplateRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 如果设为默认,先取消其他默认模板 if req.IsDefault { utils.DB.Model(&models.ShippingTemplate{}).Where("is_default = ?", true).Update("is_default", false) } // 序列化省份列表 var provincesJSON string if len(req.Provinces) > 0 { data, _ := json.Marshal(req.Provinces) provincesJSON = string(data) } template := models.ShippingTemplate{ Name: req.Name, CalcType: req.CalcType, FirstUnit: req.FirstUnit, FirstFee: req.FirstFee, AdditionalUnit: req.AdditionalUnit, AdditionalFee: req.AdditionalFee, FreeAmount: req.FreeAmount, Provinces: provincesJSON, IsDefault: req.IsDefault, SortOrder: req.SortOrder, } if err := utils.DB.Create(&template).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "创建运费模板失败"}) return } c.JSON(http.StatusCreated, gin.H{"data": template}) } // Update 更新运费模板 func (h *ShippingTemplateHandler) Update(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) var template models.ShippingTemplate if err := utils.DB.First(&template, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "运费模板不存在"}) return } var req schemas.UpdateShippingTemplateRequest 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.CalcType != nil { updates["calc_type"] = *req.CalcType } if req.FirstUnit != nil { updates["first_unit"] = *req.FirstUnit } if req.FirstFee != nil { updates["first_fee"] = *req.FirstFee } if req.AdditionalUnit != nil { updates["additional_unit"] = *req.AdditionalUnit } if req.AdditionalFee != nil { updates["additional_fee"] = *req.AdditionalFee } if req.FreeAmount != nil { updates["free_amount"] = *req.FreeAmount } if req.Provinces != nil { if len(req.Provinces) > 0 { data, _ := json.Marshal(req.Provinces) updates["provinces"] = string(data) } else { updates["provinces"] = "" } } if req.IsDefault != nil { if *req.IsDefault { utils.DB.Model(&models.ShippingTemplate{}).Where("is_default = ? AND id != ?", true, id).Update("is_default", false) } updates["is_default"] = *req.IsDefault } if req.SortOrder != nil { updates["sort_order"] = *req.SortOrder } utils.DB.Model(&template).Updates(updates) utils.DB.First(&template, id) c.JSON(http.StatusOK, gin.H{"data": template}) } // Delete 删除运费模板 func (h *ShippingTemplateHandler) Delete(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) var template models.ShippingTemplate if err := utils.DB.First(&template, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "运费模板不存在"}) return } // 检查是否有关联的商品 var count int64 utils.DB.Model(&models.Product{}).Where("shipping_template_id = ?", id).Count(&count) if count > 0 { c.JSON(http.StatusBadRequest, gin.H{"error": "该运费模板已被商品关联,无法删除"}) return } if err := utils.DB.Delete(&template).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "删除运费模板失败"}) return } c.JSON(http.StatusOK, gin.H{"message": "删除成功"}) } // CalculateShippingFee 计算运费(供内部调用) func CalculateShippingFee(templateID *uint, weight float64, quantity int, subtotal float64, province string) (float64, error) { var template models.ShippingTemplate // 如果没有指定模板,使用默认模板 if templateID == nil || *templateID == 0 { if err := utils.DB.Where("is_default = ?", true).First(&template).Error; err != nil { // 没有默认模板,运费为0 return 0, nil } } else { if err := utils.DB.First(&template, *templateID).Error; err != nil { // 模板不存在,使用默认模板 if err := utils.DB.Where("is_default = ?", true).First(&template).Error; err != nil { return 0, nil } } } // 检查适用地区 if template.Provinces != "" && province != "" { var provinces []string json.Unmarshal([]byte(template.Provinces), &provinces) matched := false for _, p := range provinces { if p == province { matched = true break } } if !matched { // 地址不在适用范围内,使用默认模板 var defaultTemplate models.ShippingTemplate if err := utils.DB.Where("is_default = ?", true).First(&defaultTemplate).Error; err == nil { template = defaultTemplate } } } // 检查是否满足包邮条件 if template.FreeAmount > 0 && subtotal >= template.FreeAmount { return 0, nil } // 根据计费方式计算运费 var totalUnit float64 if template.CalcType == models.CalcTypeWeight { totalUnit = weight // 重量(克) } else { totalUnit = float64(quantity) // 件数 } // 计算运费 if totalUnit <= template.FirstUnit { return template.FirstFee, nil } remainingUnit := totalUnit - template.FirstUnit additionalCount := math.Ceil(remainingUnit / template.AdditionalUnit) return template.FirstFee + additionalCount*template.AdditionalFee, nil }