feat: add shipping template management and update favicon
Build and Push Docker Image / build-and-push (push) Successful in 1m3s
Build and Push Docker Image / deploy (push) Successful in 7s

This commit is contained in:
nuyue
2026-07-12 18:52:27 +08:00
parent 6476d20faa
commit 46daa0999b
16 changed files with 865 additions and 99 deletions
+73 -16
View File
@@ -300,14 +300,80 @@ func (h *OrderHandler) Create(c *gin.Context) {
break
}
var shippingFeeFirstWeight, shippingFeePerGram, serviceFeeRate, taxRate, channelFeeRate float64
// 获取收货地址省份
var shippingAddress models.Address
var province string
if err := utils.DB.First(&shippingAddress, req.ShippingAddressID).Error; err == nil {
province = shippingAddress.Province
}
// 使用运费模板计算运费
shippingFee := 0.0
// 按运费模板分组计算
type TemplateGroup struct {
TemplateID *uint
Weight float64
Quantity int
Subtotal float64
}
templateGroups := make(map[uint]*TemplateGroup)
noTemplateGroup := &TemplateGroup{Subtotal: 0, Weight: 0, Quantity: 0}
for _, cart := range carts {
var templateID uint
if cart.Product.ShippingTemplateID != nil {
templateID = *cart.Product.ShippingTemplateID
}
if templateID == 0 {
// 没有运费模板,累加到无模板组
noTemplateGroup.Weight += cart.Product.Weight * float64(cart.Quantity)
noTemplateGroup.Quantity += cart.Quantity
noTemplateGroup.Subtotal += cart.Product.Price * float64(cart.Quantity)
} else {
if _, ok := templateGroups[templateID]; !ok {
templateGroups[templateID] = &TemplateGroup{
TemplateID: &templateID,
Weight: 0,
Quantity: 0,
Subtotal: 0,
}
}
templateGroups[templateID].Weight += cart.Product.Weight * float64(cart.Quantity)
templateGroups[templateID].Quantity += cart.Quantity
templateGroups[templateID].Subtotal += cart.Product.Price * float64(cart.Quantity)
}
}
// 计算有模板的运费
for _, group := range templateGroups {
fee, _ := CalculateShippingFee(group.TemplateID, group.Weight, group.Quantity, group.Subtotal, province)
shippingFee += fee
}
// 计算无模板的运费(使用旧的系统设置)
if noTemplateGroup.Subtotal > 0 {
var shippingFeeFirstWeight, shippingFeePerGram float64
var setting models.SystemSetting
if err := utils.DB.Where("`key` = ?", "shipping_fee_first_weight").First(&setting).Error; err == nil {
shippingFeeFirstWeight, _ = strconv.ParseFloat(setting.Value, 64)
}
if err := utils.DB.Where("`key` = ?", "shipping_fee_per_gram").First(&setting).Error; err == nil {
shippingFeePerGram, _ = strconv.ParseFloat(setting.Value, 64)
}
if noTemplateGroup.Subtotal < 99 && shippingFeeFirstWeight > 0 {
fee := shippingFeeFirstWeight
if noTemplateGroup.Weight > 500 {
fee += shippingFeePerGram * (noTemplateGroup.Weight - 500)
}
shippingFee += fee
}
}
// 获取其他费率
var serviceFeeRate, taxRate, channelFeeRate float64
var setting models.SystemSetting
if err := utils.DB.Where("`key` = ?", "shipping_fee_first_weight").First(&setting).Error; err == nil {
shippingFeeFirstWeight, _ = strconv.ParseFloat(setting.Value, 64)
}
if err := utils.DB.Where("`key` = ?", "shipping_fee_per_gram").First(&setting).Error; err == nil {
shippingFeePerGram, _ = strconv.ParseFloat(setting.Value, 64)
}
if err := utils.DB.Where("`key` = ?", "service_fee_rate").First(&setting).Error; err == nil {
serviceFeeRate, _ = strconv.ParseFloat(setting.Value, 64)
}
@@ -318,15 +384,6 @@ func (h *OrderHandler) Create(c *gin.Context) {
channelFeeRate, _ = strconv.ParseFloat(setting.Value, 64)
}
// 根据商品重量计算运费
shippingFee := 0.0
if subtotal < 99 && shippingFeeFirstWeight > 0 {
shippingFee = shippingFeeFirstWeight
if totalWeight > 500 {
shippingFee += shippingFeePerGram * (totalWeight - 500)
}
}
serviceFee := subtotal * serviceFeeRate / 100
tax := subtotal * taxRate / 100
channelFee := subtotal * channelFeeRate / 100
+23 -19
View File
@@ -225,7 +225,7 @@ func (h *ProductHandler) GetByID(c *gin.Context) {
func (h *ProductHandler) AdminList(c *gin.Context) {
var products []models.Product
utils.DB.Preload("Categories").Preload("Brand").Order("created_at DESC").Find(&products)
utils.DB.Preload("Categories").Preload("Brand").Preload("ShippingTemplate").Order("created_at DESC").Find(&products)
c.JSON(http.StatusOK, gin.H{"data": products})
}
@@ -237,22 +237,23 @@ func (h *ProductHandler) Create(c *gin.Context) {
}
product := models.Product{
Name: req.Name,
Description: req.Description,
Price: req.Price,
Weight: req.Weight,
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,
Name: req.Name,
Description: req.Description,
Price: req.Price,
Weight: req.Weight,
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,
ShippingTemplateID: req.ShippingTemplateID,
IsActive: true,
}
if err := utils.DB.Create(&product).Error; err != nil {
@@ -276,7 +277,7 @@ func (h *ProductHandler) Create(c *gin.Context) {
}
}
utils.DB.Preload("Categories").Preload("CustomFields").Preload("Brand").First(&product, product.ID)
utils.DB.Preload("Categories").Preload("CustomFields").Preload("Brand").Preload("ShippingTemplate").First(&product, product.ID)
c.JSON(http.StatusCreated, gin.H{"data": product})
}
@@ -343,6 +344,9 @@ func (h *ProductHandler) Update(c *gin.Context) {
if req.BrandID != nil {
updates["brand_id"] = *req.BrandID
}
if req.ShippingTemplateID != nil {
updates["shipping_template_id"] = *req.ShippingTemplateID
}
utils.DB.Model(&product).Updates(updates)
@@ -363,7 +367,7 @@ func (h *ProductHandler) Update(c *gin.Context) {
}
}
utils.DB.Preload("Categories").Preload("CustomFields").Preload("Brand").First(&product, product.ID)
utils.DB.Preload("Categories").Preload("CustomFields").Preload("Brand").Preload("ShippingTemplate").First(&product, product.ID)
c.JSON(http.StatusOK, gin.H{"data": product})
}
@@ -0,0 +1,260 @@
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
}
+10
View File
@@ -27,6 +27,7 @@ func SetupRoutes(r *gin.Engine) {
paymentHandler := handlers.NewPaymentHandler()
logHandler := handlers.NewLogHandler()
purchaseGroupHandler := handlers.NewPurchaseGroupHandler()
shippingTemplateHandler := handlers.NewShippingTemplateHandler()
r.Use(middlewares.CORSMiddleware())
@@ -250,6 +251,15 @@ func SetupRoutes(r *gin.Engine) {
admin.GET("/logs", logHandler.GetLogs)
admin.DELETE("/logs", logHandler.ClearLogs)
admin.GET("/logs/info", logHandler.GetLogInfo)
adminShippingTemplates := admin.Group("/shipping-templates")
{
adminShippingTemplates.GET("", shippingTemplateHandler.List)
adminShippingTemplates.GET("/:id", shippingTemplateHandler.GetByID)
adminShippingTemplates.POST("", shippingTemplateHandler.Create)
adminShippingTemplates.PUT("/:id", shippingTemplateHandler.Update)
adminShippingTemplates.DELETE("/:id", shippingTemplateHandler.Delete)
}
}
}
}
+25 -23
View File
@@ -7,29 +7,31 @@ import (
)
type Product struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:255;not null" json:"name"`
Description string `json:"description"`
Price float64 `gorm:"type:decimal(10,2);not null" json:"price"`
Weight float64 `gorm:"type:decimal(10,2);default:0" json:"weight"` // 商品克重(克)
MinPurchase int `gorm:"default:1" json:"min_purchase"`
MaxPurchase *int `json:"max_purchase"`
MinWeight *float64 `json:"min_weight"`
MaxWeight *float64 `json:"max_weight"`
MinAmount *float64 `json:"min_amount"`
MaxAmount *float64 `json:"max_amount"`
RequireCredit bool `gorm:"default:false" json:"require_credit"`
CreditCost int `gorm:"default:0" json:"credit_cost"`
CreditReward int `gorm:"default:0" json:"credit_reward"`
Images string `gorm:"type:text" json:"images"`
IsActive bool `gorm:"default:true" json:"is_active"`
BrandID *uint `json:"brand_id"`
Brand *Brand `json:"brand,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
Categories []Category `gorm:"many2many:product_categories;" json:"categories,omitempty"`
CustomFields []ProductCustomField `json:"custom_fields,omitempty"`
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:255;not null" json:"name"`
Description string `json:"description"`
Price float64 `gorm:"type:decimal(10,2);not null" json:"price"`
Weight float64 `gorm:"type:decimal(10,2);default:0" json:"weight"` // 商品克重(克)
MinPurchase int `gorm:"default:1" json:"min_purchase"`
MaxPurchase *int `json:"max_purchase"`
MinWeight *float64 `json:"min_weight"`
MaxWeight *float64 `json:"max_weight"`
MinAmount *float64 `json:"min_amount"`
MaxAmount *float64 `json:"max_amount"`
RequireCredit bool `gorm:"default:false" json:"require_credit"`
CreditCost int `gorm:"default:0" json:"credit_cost"`
CreditReward int `gorm:"default:0" json:"credit_reward"`
Images string `gorm:"type:text" json:"images"`
IsActive bool `gorm:"default:true" json:"is_active"`
BrandID *uint `json:"brand_id"`
Brand *Brand `json:"brand,omitempty"`
ShippingTemplateID *uint `json:"shipping_template_id"`
ShippingTemplate *ShippingTemplate `json:"shipping_template,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
Categories []Category `gorm:"many2many:product_categories;" json:"categories,omitempty"`
CustomFields []ProductCustomField `json:"custom_fields,omitempty"`
}
func (Product) TableName() string {
@@ -0,0 +1,35 @@
package models
import (
"time"
"gorm.io/gorm"
)
// ShippingTemplate 运费模板
type ShippingTemplate struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:255;not null" json:"name"` // 模板名称(如"江浙沪包邮"、"偏远地区"
CalcType string `gorm:"size:20;not null;default:'weight'" json:"calc_type"` // 计费方式:weight=按重量, piece=按件数
FirstUnit float64 `gorm:"type:decimal(10,2);not null" json:"first_unit"` // 首重/首件数量
FirstFee float64 `gorm:"type:decimal(10,2);not null" json:"first_fee"` // 首重/首件价格
AdditionalUnit float64 `gorm:"type:decimal(10,2);not null" json:"additional_unit"` // 续重/续件数量
AdditionalFee float64 `gorm:"type:decimal(10,2);not null" json:"additional_fee"` // 续重/续件价格
FreeAmount float64 `gorm:"type:decimal(10,2);default:0" json:"free_amount"` // 包邮金额(0表示不包邮)
Provinces string `gorm:"type:text" json:"provinces"` // 适用省份列表(JSON数组,为空表示全国)
IsDefault bool `gorm:"default:false" json:"is_default"` // 是否默认模板
SortOrder int `gorm:"default:0" json:"sort_order"` // 排序
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
func (ShippingTemplate) TableName() string {
return "shipping_templates"
}
// 计费方式常量
const (
CalcTypeWeight = "weight" // 按重量计费
CalcTypePiece = "piece" // 按件数计费
)
+37 -35
View File
@@ -41,44 +41,46 @@ type UpdatePurchaseGroupRequest struct {
}
type CreateProductRequest struct {
Name string `json:"name" binding:"required"`
Description string `json:"description"`
Price float64 `json:"price" binding:"gte=0"`
Weight float64 `json:"weight"`
MinPurchase int `json:"min_purchase"`
MaxPurchase *int `json:"max_purchase"`
MinWeight *float64 `json:"min_weight"`
MaxWeight *float64 `json:"max_weight"`
MinAmount *float64 `json:"min_amount"`
MaxAmount *float64 `json:"max_amount"`
RequireCredit bool `json:"require_credit"`
CreditCost int `json:"credit_cost"`
CreditReward int `json:"credit_reward"`
Images string `json:"images"`
BrandID *uint `json:"brand_id"`
CategoryIDs []uint `json:"category_ids"`
CustomFields []CustomFieldRequest `json:"custom_fields"`
Name string `json:"name" binding:"required"`
Description string `json:"description"`
Price float64 `json:"price" binding:"gte=0"`
Weight float64 `json:"weight"`
MinPurchase int `json:"min_purchase"`
MaxPurchase *int `json:"max_purchase"`
MinWeight *float64 `json:"min_weight"`
MaxWeight *float64 `json:"max_weight"`
MinAmount *float64 `json:"min_amount"`
MaxAmount *float64 `json:"max_amount"`
RequireCredit bool `json:"require_credit"`
CreditCost int `json:"credit_cost"`
CreditReward int `json:"credit_reward"`
Images string `json:"images"`
BrandID *uint `json:"brand_id"`
ShippingTemplateID *uint `json:"shipping_template_id"`
CategoryIDs []uint `json:"category_ids"`
CustomFields []CustomFieldRequest `json:"custom_fields"`
}
type UpdateProductRequest struct {
Name *string `json:"name"`
Description *string `json:"description"`
Price *float64 `json:"price"`
Weight *float64 `json:"weight"`
MinPurchase *int `json:"min_purchase"`
MaxPurchase *int `json:"max_purchase"`
MinWeight *float64 `json:"min_weight"`
MaxWeight *float64 `json:"max_weight"`
MinAmount *float64 `json:"min_amount"`
MaxAmount *float64 `json:"max_amount"`
RequireCredit *bool `json:"require_credit"`
CreditCost *int `json:"credit_cost"`
CreditReward *int `json:"credit_reward"`
Images *string `json:"images"`
IsActive *bool `json:"is_active"`
BrandID *uint `json:"brand_id"`
CategoryIDs []uint `json:"category_ids"`
CustomFields []CustomFieldRequest `json:"custom_fields"`
Name *string `json:"name"`
Description *string `json:"description"`
Price *float64 `json:"price"`
Weight *float64 `json:"weight"`
MinPurchase *int `json:"min_purchase"`
MaxPurchase *int `json:"max_purchase"`
MinWeight *float64 `json:"min_weight"`
MaxWeight *float64 `json:"max_weight"`
MinAmount *float64 `json:"min_amount"`
MaxAmount *float64 `json:"max_amount"`
RequireCredit *bool `json:"require_credit"`
CreditCost *int `json:"credit_cost"`
CreditReward *int `json:"credit_reward"`
Images *string `json:"images"`
IsActive *bool `json:"is_active"`
BrandID *uint `json:"brand_id"`
ShippingTemplateID *uint `json:"shipping_template_id"`
CategoryIDs []uint `json:"category_ids"`
CustomFields []CustomFieldRequest `json:"custom_fields"`
}
type CustomFieldRequest struct {
@@ -0,0 +1,29 @@
package schemas
// CreateShippingTemplateRequest 创建运费模板请求
type CreateShippingTemplateRequest struct {
Name string `json:"name" binding:"required"`
CalcType string `json:"calc_type" binding:"required,oneof=weight piece"`
FirstUnit float64 `json:"first_unit" binding:"required,gt=0"`
FirstFee float64 `json:"first_fee" binding:"gte=0"`
AdditionalUnit float64 `json:"additional_unit" binding:"required,gt=0"`
AdditionalFee float64 `json:"additional_fee" binding:"gte=0"`
FreeAmount float64 `json:"free_amount"`
Provinces []string `json:"provinces"` // 省份列表
IsDefault bool `json:"is_default"`
SortOrder int `json:"sort_order"`
}
// UpdateShippingTemplateRequest 更新运费模板请求
type UpdateShippingTemplateRequest struct {
Name *string `json:"name"`
CalcType *string `json:"calc_type" binding:"omitempty,oneof=weight piece"`
FirstUnit *float64 `json:"first_unit" binding:"omitempty,gt=0"`
FirstFee *float64 `json:"first_fee" binding:"omitempty,gte=0"`
AdditionalUnit *float64 `json:"additional_unit" binding:"omitempty,gt=0"`
AdditionalFee *float64 `json:"additional_fee" binding:"omitempty,gte=0"`
FreeAmount *float64 `json:"free_amount"`
Provinces []string `json:"provinces"`
IsDefault *bool `json:"is_default"`
SortOrder *int `json:"sort_order"`
}