322 lines
11 KiB
Go
322 lines
11 KiB
Go
|
|
package handler
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"dianshang/internal/service"
|
|||
|
|
"dianshang/pkg/response"
|
|||
|
|
"dianshang/pkg/utils"
|
|||
|
|
"fmt"
|
|||
|
|
|
|||
|
|
"github.com/gin-gonic/gin"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// OrderSettleHandler 订单结算处理器
|
|||
|
|
type OrderSettleHandler struct {
|
|||
|
|
orderService *service.OrderService
|
|||
|
|
productService *service.ProductService
|
|||
|
|
userService *service.UserService
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewOrderSettleHandler 创建订单结算处理器
|
|||
|
|
func NewOrderSettleHandler(orderService *service.OrderService, productService *service.ProductService, userService *service.UserService) *OrderSettleHandler {
|
|||
|
|
return &OrderSettleHandler{
|
|||
|
|
orderService: orderService,
|
|||
|
|
productService: productService,
|
|||
|
|
userService: userService,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// SettleOrderRequest 订单结算请求
|
|||
|
|
type SettleOrderRequest struct {
|
|||
|
|
UserAddressReq *UserAddressRequest `json:"userAddressReq"`
|
|||
|
|
CouponList []CouponRequest `json:"couponList"`
|
|||
|
|
GoodsRequestList []GoodsRequest `json:"goodsRequestList"`
|
|||
|
|
StoreInfoList []StoreInfo `json:"storeInfoList"` // 添加门店信息列表
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// UserAddressRequest 用户地址请求
|
|||
|
|
type UserAddressRequest struct {
|
|||
|
|
AddressID uint `json:"addressId"`
|
|||
|
|
Name string `json:"name"`
|
|||
|
|
Phone string `json:"phone"`
|
|||
|
|
Address string `json:"address"`
|
|||
|
|
DetailAddress string `json:"detailAddress"` // 添加详细地址字段,支持前端address-card组件
|
|||
|
|
ProvinceCode string `json:"provinceCode"`
|
|||
|
|
CityCode string `json:"cityCode"`
|
|||
|
|
DistrictCode string `json:"districtCode"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// CouponRequest 优惠券请求
|
|||
|
|
type CouponRequest struct {
|
|||
|
|
CouponID uint `json:"couponId"` // 优惠券模板ID
|
|||
|
|
UserCouponID uint `json:"userCouponId"` // 用户优惠券ID (ai_user_coupons表的id)
|
|||
|
|
Type int `json:"type"`
|
|||
|
|
Value int `json:"value"`
|
|||
|
|
Status string `json:"status"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// StoreInfo 门店信息
|
|||
|
|
type StoreInfo struct {
|
|||
|
|
StoreID string `json:"storeId"`
|
|||
|
|
StoreName string `json:"storeName"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GoodsRequest 商品请求
|
|||
|
|
type GoodsRequest struct {
|
|||
|
|
StoreID string `json:"storeId"`
|
|||
|
|
SpuID string `json:"spuId"`
|
|||
|
|
SkuID interface{} `json:"skuId"` // 改为interface{}以支持数字和字符串
|
|||
|
|
Title string `json:"title"`
|
|||
|
|
GoodsName string `json:"goodsName"` // 添加商品名称字段
|
|||
|
|
PrimaryImage string `json:"primaryImage"`
|
|||
|
|
Thumb string `json:"thumb"` // 添加缩略图字段
|
|||
|
|
Quantity int `json:"quantity"`
|
|||
|
|
Price int `json:"price"`
|
|||
|
|
OriginPrice int `json:"originPrice"`
|
|||
|
|
Available int `json:"available"` // 添加可用性字段
|
|||
|
|
SpecInfo []SpecInfo `json:"specInfo"`
|
|||
|
|
RoomID interface{} `json:"roomId"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// SpecInfo 规格信息
|
|||
|
|
type SpecInfo struct {
|
|||
|
|
SpecID string `json:"specId"`
|
|||
|
|
SpecTitle string `json:"specTitle"`
|
|||
|
|
SpecValue string `json:"specValue"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// SettleOrder 订单结算
|
|||
|
|
func (h *OrderSettleHandler) SettleOrder(c *gin.Context) {
|
|||
|
|
_, exists := c.Get("user_id")
|
|||
|
|
if !exists {
|
|||
|
|
response.Unauthorized(c)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var req SettleOrderRequest
|
|||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|||
|
|
response.BadRequest(c, "参数错误: "+err.Error())
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证商品信息并计算价格
|
|||
|
|
var totalAmount float64
|
|||
|
|
var totalGoodsCount int
|
|||
|
|
var skuDetailVos []map[string]interface{}
|
|||
|
|
|
|||
|
|
for _, goods := range req.GoodsRequestList {
|
|||
|
|
// 转换SkuID为字符串
|
|||
|
|
var skuIDStr string
|
|||
|
|
switch v := goods.SkuID.(type) {
|
|||
|
|
case string:
|
|||
|
|
skuIDStr = v
|
|||
|
|
case float64:
|
|||
|
|
skuIDStr = fmt.Sprintf("%.0f", v)
|
|||
|
|
case int:
|
|||
|
|
skuIDStr = fmt.Sprintf("%d", v)
|
|||
|
|
default:
|
|||
|
|
skuIDStr = fmt.Sprintf("%v", v)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
spuID := utils.StringToUint(goods.SpuID)
|
|||
|
|
if spuID == 0 {
|
|||
|
|
response.BadRequest(c, "无效的商品ID: "+goods.SpuID)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取商品信息
|
|||
|
|
product, err := h.productService.GetProductDetail(spuID)
|
|||
|
|
if err != nil {
|
|||
|
|
response.BadRequest(c, "商品不存在: "+goods.SpuID)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查库存
|
|||
|
|
if product.Stock < goods.Quantity {
|
|||
|
|
response.BadRequest(c, "商品库存不足: "+product.Name)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取实际价格:优先使用SKU价格,如果没有SKU则使用SPU价格
|
|||
|
|
var actualPrice float64
|
|||
|
|
skuID := utils.StringToUint(skuIDStr)
|
|||
|
|
if skuID > 0 {
|
|||
|
|
// 根据SKU ID获取SKU详情和价格
|
|||
|
|
sku, err := h.productService.GetSKUByID(skuID)
|
|||
|
|
if err != nil {
|
|||
|
|
// 如果SKU不存在,使用SPU价格
|
|||
|
|
actualPrice = product.Price
|
|||
|
|
} else {
|
|||
|
|
// 使用SKU价格
|
|||
|
|
actualPrice = sku.Price
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 没有SKU ID,使用SPU价格
|
|||
|
|
actualPrice = product.Price
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算商品总价(数据库中价格以分为单位存储,直接计算)
|
|||
|
|
itemTotal := actualPrice * float64(goods.Quantity)
|
|||
|
|
totalAmount += itemTotal
|
|||
|
|
totalGoodsCount += goods.Quantity
|
|||
|
|
|
|||
|
|
// 构建商品详情
|
|||
|
|
skuDetail := map[string]interface{}{
|
|||
|
|
"storeId": goods.StoreID,
|
|||
|
|
"spuId": goods.SpuID,
|
|||
|
|
"skuId": skuIDStr,
|
|||
|
|
"goodsName": product.Name,
|
|||
|
|
"image": product.MainImage,
|
|||
|
|
"reminderStock": product.Stock,
|
|||
|
|
"quantity": goods.Quantity,
|
|||
|
|
"payPrice": actualPrice,
|
|||
|
|
"totalSkuPrice": itemTotal,
|
|||
|
|
"discountSettlePrice": actualPrice,
|
|||
|
|
"realSettlePrice": actualPrice,
|
|||
|
|
"settlePrice": actualPrice,
|
|||
|
|
"oriPrice": actualPrice,
|
|||
|
|
"tagPrice": nil,
|
|||
|
|
"tagText": nil,
|
|||
|
|
"skuSpecLst": goods.SpecInfo,
|
|||
|
|
"promotionIds": nil,
|
|||
|
|
"weight": 0.0,
|
|||
|
|
"unit": "KG",
|
|||
|
|
"volume": nil,
|
|||
|
|
"masterGoodsType": 0,
|
|||
|
|
"viceGoodsType": 0,
|
|||
|
|
"roomId": goods.RoomID,
|
|||
|
|
"egoodsName": nil,
|
|||
|
|
}
|
|||
|
|
skuDetailVos = append(skuDetailVos, skuDetail)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算优惠券折扣
|
|||
|
|
var totalCouponAmount float64
|
|||
|
|
for _, coupon := range req.CouponList {
|
|||
|
|
if coupon.Status == "default" {
|
|||
|
|
if coupon.Type == 1 {
|
|||
|
|
// 固定金额优惠券(数据库中value以分为单位存储,直接累加)
|
|||
|
|
totalCouponAmount += float64(coupon.Value)
|
|||
|
|
} else if coupon.Type == 2 {
|
|||
|
|
// 折扣优惠券 - 修复:计算折扣金额而不是最终价格
|
|||
|
|
// 例如:8折券(value=80),9900分商品的折扣金额 = 9900 * (100-80) / 100 = 1980分
|
|||
|
|
totalCouponAmount += totalAmount * (100 - float64(coupon.Value)) / 100
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算最终支付金额
|
|||
|
|
totalPromotionAmount := 0.0 // 暂时没有促销活动
|
|||
|
|
totalDeliveryFee := 0.0 // 免运费
|
|||
|
|
totalPayAmount := totalAmount - totalCouponAmount - totalPromotionAmount + totalDeliveryFee
|
|||
|
|
|
|||
|
|
// 获取用户地址信息
|
|||
|
|
var userAddress interface{}
|
|||
|
|
settleType := 0
|
|||
|
|
if req.UserAddressReq != nil {
|
|||
|
|
settleType = 1
|
|||
|
|
|
|||
|
|
// 如果有地址ID,从数据库获取完整的地址信息
|
|||
|
|
if req.UserAddressReq.AddressID > 0 {
|
|||
|
|
userID, exists := c.Get("user_id")
|
|||
|
|
if exists {
|
|||
|
|
// 获取完整的地址信息
|
|||
|
|
fullAddress, err := h.userService.GetAddressByID(userID.(uint), req.UserAddressReq.AddressID)
|
|||
|
|
if err == nil && fullAddress != nil {
|
|||
|
|
// 构建包含完整信息的地址对象
|
|||
|
|
userAddress = map[string]interface{}{
|
|||
|
|
"addressId": fullAddress.ID,
|
|||
|
|
"name": fullAddress.Name,
|
|||
|
|
"phone": fullAddress.Phone,
|
|||
|
|
"address": fmt.Sprintf("%s%s%s%s", fullAddress.ProvinceName, fullAddress.CityName, fullAddress.DistrictName, fullAddress.DetailAddress),
|
|||
|
|
"detailAddress": fullAddress.DetailAddress,
|
|||
|
|
"provinceName": fullAddress.ProvinceName,
|
|||
|
|
"cityName": fullAddress.CityName,
|
|||
|
|
"districtName": fullAddress.DistrictName,
|
|||
|
|
"provinceCode": req.UserAddressReq.ProvinceCode,
|
|||
|
|
"cityCode": req.UserAddressReq.CityCode,
|
|||
|
|
"districtCode": req.UserAddressReq.DistrictCode,
|
|||
|
|
"isDefault": fullAddress.IsDefault,
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 如果获取失败,使用原始请求数据
|
|||
|
|
userAddress = req.UserAddressReq
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 如果没有用户ID,使用原始请求数据
|
|||
|
|
userAddress = req.UserAddressReq
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 如果没有地址ID,使用原始请求数据
|
|||
|
|
userAddress = req.UserAddressReq
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取店铺信息
|
|||
|
|
storeID := uint(1000) // 默认店铺ID,实际应该从商品信息中获取
|
|||
|
|
if len(req.GoodsRequestList) > 0 {
|
|||
|
|
// 从第一个商品获取店铺ID
|
|||
|
|
if storeIDFromGoods := utils.StringToUint(req.GoodsRequestList[0].StoreID); storeIDFromGoods > 0 {
|
|||
|
|
storeID = storeIDFromGoods
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 动态获取店铺名称
|
|||
|
|
storeName := "默认店铺" // 默认店铺名
|
|||
|
|
store, err := h.productService.GetStoreByID(storeID)
|
|||
|
|
if err == nil && store != nil {
|
|||
|
|
storeName = store.Name
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 构建返回数据(前端期望接收以分为单位的金额字符串)
|
|||
|
|
result := map[string]interface{}{
|
|||
|
|
"settleType": settleType,
|
|||
|
|
"userAddress": userAddress,
|
|||
|
|
"totalGoodsCount": totalGoodsCount,
|
|||
|
|
"packageCount": 1,
|
|||
|
|
"totalAmount": utils.FloatToString(totalAmount), // 已经是分为单位
|
|||
|
|
"totalPayAmount": utils.FloatToString(totalPayAmount), // 已经是分为单位
|
|||
|
|
"totalDiscountAmount": "0",
|
|||
|
|
"totalPromotionAmount": utils.FloatToString(totalPromotionAmount), // 已经是分为单位
|
|||
|
|
"totalCouponAmount": utils.FloatToString(totalCouponAmount), // 已经是分为单位
|
|||
|
|
"totalSalePrice": utils.FloatToString(totalAmount), // 已经是分为单位
|
|||
|
|
"totalGoodsAmount": utils.FloatToString(totalAmount), // 已经是分为单位
|
|||
|
|
"totalDeliveryFee": utils.FloatToString(totalDeliveryFee), // 已经是分为单位
|
|||
|
|
"invoiceRequest": nil,
|
|||
|
|
"skuImages": nil,
|
|||
|
|
"deliveryFeeList": nil,
|
|||
|
|
"storeGoodsList": []map[string]interface{}{
|
|||
|
|
{
|
|||
|
|
"storeId": fmt.Sprintf("%d", storeID),
|
|||
|
|
"storeName": storeName,
|
|||
|
|
"remark": nil,
|
|||
|
|
"goodsCount": totalGoodsCount,
|
|||
|
|
"deliveryFee": utils.FloatToString(totalDeliveryFee), // 已经是分为单位
|
|||
|
|
"deliveryWords": nil,
|
|||
|
|
"storeTotalAmount": utils.FloatToString(totalAmount), // 已经是分为单位
|
|||
|
|
"storeTotalPayAmount": utils.FloatToString(totalPayAmount), // 已经是分为单位
|
|||
|
|
"storeTotalDiscountAmount": "0",
|
|||
|
|
"storeTotalCouponAmount": utils.FloatToString(totalCouponAmount), // 已经是分为单位
|
|||
|
|
"skuDetailVos": skuDetailVos,
|
|||
|
|
"couponList": func() []map[string]interface{} {
|
|||
|
|
var couponList []map[string]interface{}
|
|||
|
|
for _, coupon := range req.CouponList {
|
|||
|
|
if coupon.Status == "default" {
|
|||
|
|
couponList = append(couponList, map[string]interface{}{
|
|||
|
|
"couponId": coupon.UserCouponID, // 使用用户优惠券ID
|
|||
|
|
"storeId": fmt.Sprintf("%d", storeID),
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return couponList
|
|||
|
|
}(),
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
"inValidGoodsList": nil,
|
|||
|
|
"outOfStockGoodsList": nil,
|
|||
|
|
"limitGoodsList": nil,
|
|||
|
|
"abnormalDeliveryGoodsList": nil,
|
|||
|
|
"invoiceSupport": 1,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
response.Success(c, result)
|
|||
|
|
}
|