diff --git a/backend/internal/api/handlers/order.go b/backend/internal/api/handlers/order.go index a14cc19..d6a854f 100644 --- a/backend/internal/api/handlers/order.go +++ b/backend/internal/api/handlers/order.go @@ -243,6 +243,7 @@ func (h *OrderHandler) Create(c *gin.Context) { var subtotal float64 var totalQuantity int + var totalWeight float64 var orderItems []models.OrderItem supplierMap := make(map[uint]bool) @@ -280,6 +281,7 @@ func (h *OrderHandler) Create(c *gin.Context) { subtotal += cart.Product.Price * float64(cart.Quantity) totalQuantity += cart.Quantity + totalWeight += cart.Product.Weight * float64(cart.Quantity) orderItems = append(orderItems, models.OrderItem{ ProductID: cart.ProductID, Quantity: cart.Quantity, @@ -298,7 +300,7 @@ func (h *OrderHandler) Create(c *gin.Context) { break } - var shippingFeeFirstWeight, shippingFeePerGram, serviceFeeRate, taxRate float64 + var shippingFeeFirstWeight, shippingFeePerGram, 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) @@ -312,17 +314,23 @@ func (h *OrderHandler) Create(c *gin.Context) { if err := utils.DB.Where("`key` = ?", "tax_rate").First(&setting).Error; err == nil { taxRate, _ = strconv.ParseFloat(setting.Value, 64) } + if err := utils.DB.Where("`key` = ?", "payment_channel_fee_rate").First(&setting).Error; err == nil { + channelFeeRate, _ = strconv.ParseFloat(setting.Value, 64) + } - shippingFee := shippingFeeFirstWeight - if subtotal >= 99 || shippingFeeFirstWeight == 0 { - shippingFee = 0 - } else if totalQuantity > 500 { - shippingFee += shippingFeePerGram * float64(totalQuantity-500) + // 根据商品重量计算运费 + 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 - totalAmount := subtotal + shippingFee + serviceFee + tax + channelFee := subtotal * channelFeeRate / 100 + totalAmount := subtotal + shippingFee + serviceFee + tax + channelFee order := models.Order{ UserID: userID, @@ -331,6 +339,7 @@ func (h *OrderHandler) Create(c *gin.Context) { ShippingFee: shippingFee, ServiceFee: serviceFee, Tax: tax, + ChannelFee: channelFee, TotalAmount: totalAmount, Status: models.OrderStatusPendingPayment, ShippingAddressID: &req.ShippingAddressID, diff --git a/backend/internal/models/order.go b/backend/internal/models/order.go index 5ae67c6..28bea1b 100644 --- a/backend/internal/models/order.go +++ b/backend/internal/models/order.go @@ -14,6 +14,7 @@ type Order struct { ShippingFee float64 `gorm:"type:decimal(10,2);default:0" json:"shipping_fee"` ServiceFee float64 `gorm:"type:decimal(10,2);default:0" json:"service_fee"` Tax float64 `gorm:"type:decimal(10,2);default:0" json:"tax"` + ChannelFee float64 `gorm:"type:decimal(10,2);default:0" json:"channel_fee"` // 支付通道费 TotalAmount float64 `gorm:"type:decimal(10,2);not null" json:"total_amount"` RefundAmount *float64 `gorm:"type:decimal(10,2)" json:"refund_amount"` RefundStatus string `gorm:"size:20" json:"refund_status"` diff --git a/backend/internal/models/product.go b/backend/internal/models/product.go index 16c8c35..2f57333 100644 --- a/backend/internal/models/product.go +++ b/backend/internal/models/product.go @@ -11,6 +11,7 @@ type Product struct { 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"` diff --git a/frontend/src/views/admin/Orders.vue b/frontend/src/views/admin/Orders.vue index b4458d1..0d1c4b7 100644 --- a/frontend/src/views/admin/Orders.vue +++ b/frontend/src/views/admin/Orders.vue @@ -104,6 +104,10 @@ 税费 ¥{{ (currentOrder.tax || 0).toFixed(2) }} +
+ 通道费 + ¥{{ (currentOrder.channel_fee || 0).toFixed(2) }} +
合计 ¥{{ (currentOrder.total_amount || 0).toFixed(2) }} diff --git a/frontend/src/views/admin/Products.vue b/frontend/src/views/admin/Products.vue index 5fbff28..ae05da0 100644 --- a/frontend/src/views/admin/Products.vue +++ b/frontend/src/views/admin/Products.vue @@ -80,17 +80,22 @@
价格库存
- + - + - + + + + + + @@ -188,6 +193,7 @@ const form = reactive({ name: '', description: '', price: 0, + weight: 0, stock: 0, min_purchase: 1, max_purchase: undefined as number | undefined, @@ -264,11 +270,11 @@ async function fetchBrands() { function openAdd() { editing.value = null - Object.assign(form, { - name: '', description: '', price: 0, stock: 0, - min_purchase: 1, max_purchase: undefined, - require_credit: false, credit_cost: 0, credit_reward: 0, - is_active: true, category_ids: [], brand_id: undefined + Object.assign(form, { + name: '', description: '', price: 0, weight: 0, stock: 0, + min_purchase: 1, max_purchase: undefined, + require_credit: false, credit_cost: 0, credit_reward: 0, + is_active: true, category_ids: [], brand_id: undefined }) fileList.value = [] uploadedUrls.value = [] @@ -281,6 +287,7 @@ function editProd(row: any) { name: row.name, description: row.description || '', price: row.price, + weight: row.weight || 0, stock: row.stock || 0, min_purchase: row.min_purchase || 1, max_purchase: row.max_purchase, diff --git a/frontend/src/views/user/Cart.vue b/frontend/src/views/user/Cart.vue index f498f15..2ff2999 100644 --- a/frontend/src/views/user/Cart.vue +++ b/frontend/src/views/user/Cart.vue @@ -209,7 +209,12 @@ 税费 ({{ settings.tax_rate || 0 }}%) ¥{{ taxFee.toFixed(2) }}
- + +
+ 通道费 ({{ settings.payment_channel_fee_rate || 0 }}%) + ¥{{ channelFee.toFixed(2) }} +
+
@@ -334,11 +339,18 @@ const selectedQuantity = computed(() => { .reduce((sum, item) => sum + item.quantity, 0) }) +// 计算选中商品的总重量 +const selectedWeight = computed(() => { + return items.value + .filter(item => selectedIds.value.includes(item.id)) + .reduce((sum, item) => sum + (item.product?.weight || 0) * item.quantity, 0) +}) + const shippingFee = computed(() => { const firstWeight = parseFloat(settings.value.shipping_fee_first_weight || '0') const perGram = parseFloat(settings.value.shipping_fee_per_gram || '0') if (selectedTotal.value >= 99 || firstWeight === 0) return 0 - return firstWeight + perGram * Math.max(0, selectedQuantity.value - 500) + return firstWeight + perGram * Math.max(0, selectedWeight.value - 500) }) const serviceFee = computed(() => { @@ -351,8 +363,13 @@ const taxFee = computed(() => { return selectedTotal.value * rate / 100 }) +const channelFee = computed(() => { + const rate = parseFloat(settings.value.payment_channel_fee_rate || '0') + return selectedTotal.value * rate / 100 +}) + const grandTotal = computed(() => { - return selectedTotal.value + shippingFee.value + serviceFee.value + taxFee.value + return selectedTotal.value + shippingFee.value + serviceFee.value + taxFee.value + channelFee.value }) const canCheckout = computed(() => {