You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
256 lines
6.9 KiB
256 lines
6.9 KiB
package logic
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"healthapi/internal/model"
|
|
"healthapi/internal/svc"
|
|
"healthapi/internal/types"
|
|
"healthapi/pkg/errorx"
|
|
|
|
"github.com/zeromicro/go-zero/core/logx"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type CreateOrderLogic struct {
|
|
logx.Logger
|
|
ctx context.Context
|
|
svcCtx *svc.ServiceContext
|
|
}
|
|
|
|
func NewCreateOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateOrderLogic {
|
|
return &CreateOrderLogic{
|
|
Logger: logx.WithContext(ctx),
|
|
ctx: ctx,
|
|
svcCtx: svcCtx,
|
|
}
|
|
}
|
|
|
|
func (l *CreateOrderLogic) CreateOrder(req *types.CreateOrderReq) (resp *types.Order, err error) {
|
|
userID, err := GetUserIDFromCtx(l.ctx)
|
|
if err != nil {
|
|
return nil, errorx.ErrUnauthorized
|
|
}
|
|
|
|
// 获取用户信息(会员等级)
|
|
var user model.User
|
|
if err := l.svcCtx.DB.First(&user, userID).Error; err != nil {
|
|
return nil, errorx.ErrUserNotFound
|
|
}
|
|
levelConfig := model.MemberLevelConfigs[user.MemberLevel]
|
|
if levelConfig.Level == "" {
|
|
levelConfig = model.MemberLevelConfigs[model.MemberLevelNormal]
|
|
}
|
|
|
|
// 验证收货地址
|
|
var address model.Address
|
|
if err := l.svcCtx.DB.Where("id = ? AND user_id = ?", req.AddressID, userID).First(&address).Error; err != nil {
|
|
return nil, errorx.NewCodeError(404, "收货地址不存在")
|
|
}
|
|
|
|
// 获取购物车项
|
|
var cartItems []model.CartItem
|
|
if err := l.svcCtx.DB.Where("id IN ? AND user_id = ?", req.CartItemIDs, userID).Find(&cartItems).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
if len(cartItems) == 0 {
|
|
return nil, errorx.NewCodeError(400, "购物车为空")
|
|
}
|
|
|
|
// 计算订单金额
|
|
var totalAmount float64
|
|
orderItems := make([]model.OrderItem, 0, len(cartItems))
|
|
|
|
for _, cartItem := range cartItems {
|
|
var product model.Product
|
|
if err := l.svcCtx.DB.First(&product, cartItem.ProductID).Error; err != nil {
|
|
return nil, errorx.NewCodeError(404, fmt.Sprintf("商品 %d 不存在", cartItem.ProductID))
|
|
}
|
|
if !product.IsActive {
|
|
return nil, errorx.NewCodeError(400, fmt.Sprintf("商品 %s 已下架", product.Name))
|
|
}
|
|
|
|
price := product.Price
|
|
stock := product.Stock
|
|
skuName := ""
|
|
image := product.MainImage
|
|
|
|
if cartItem.SkuID > 0 {
|
|
var sku model.ProductSku
|
|
if err := l.svcCtx.DB.First(&sku, cartItem.SkuID).Error; err != nil {
|
|
return nil, errorx.NewCodeError(404, "商品规格不存在")
|
|
}
|
|
price = sku.Price
|
|
stock = sku.Stock
|
|
skuName = sku.Name
|
|
if sku.Image != "" {
|
|
image = sku.Image
|
|
}
|
|
}
|
|
|
|
if stock < cartItem.Quantity {
|
|
return nil, errorx.NewCodeError(400, fmt.Sprintf("商品 %s 库存不足", product.Name))
|
|
}
|
|
|
|
itemTotal := price * float64(cartItem.Quantity)
|
|
totalAmount += itemTotal
|
|
|
|
orderItems = append(orderItems, model.OrderItem{
|
|
ProductID: cartItem.ProductID,
|
|
SkuID: cartItem.SkuID,
|
|
ProductName: product.Name,
|
|
SkuName: skuName,
|
|
Image: image,
|
|
Price: price,
|
|
Quantity: cartItem.Quantity,
|
|
TotalAmount: itemTotal,
|
|
})
|
|
}
|
|
|
|
// 计算会员折扣
|
|
discountAmount := totalAmount * (1 - levelConfig.Discount)
|
|
|
|
// 计算运费
|
|
shippingFee := float64(0)
|
|
afterDiscount := totalAmount - discountAmount
|
|
if afterDiscount < levelConfig.FreeShippingMin {
|
|
shippingFee = 10 // 默认运费
|
|
}
|
|
|
|
// 计算积分抵扣(100积分=1元)
|
|
pointsDiscount := float64(0)
|
|
pointsUsed := req.PointsUsed
|
|
if pointsUsed > 0 {
|
|
if pointsUsed > user.Points {
|
|
pointsUsed = user.Points
|
|
}
|
|
maxPointsDiscount := afterDiscount * 0.2 // 最多抵扣20%
|
|
pointsDiscount = float64(pointsUsed) / 100
|
|
if pointsDiscount > maxPointsDiscount {
|
|
pointsDiscount = maxPointsDiscount
|
|
pointsUsed = int(maxPointsDiscount * 100)
|
|
}
|
|
}
|
|
|
|
// 最终支付金额
|
|
payAmount := afterDiscount + shippingFee - pointsDiscount
|
|
|
|
// 生成订单号
|
|
orderNo := fmt.Sprintf("%s%d%04d", time.Now().Format("20060102150405"), userID, time.Now().Nanosecond()%10000)
|
|
|
|
// 计算获得积分
|
|
pointsEarned := int(payAmount * levelConfig.PointsMultiplier)
|
|
|
|
// 创建订单(使用事务)
|
|
order := model.Order{
|
|
UserID: userID,
|
|
OrderNo: orderNo,
|
|
Status: model.OrderStatusPending,
|
|
TotalAmount: totalAmount,
|
|
DiscountAmount: discountAmount,
|
|
ShippingFee: shippingFee,
|
|
PayAmount: payAmount,
|
|
PointsUsed: pointsUsed,
|
|
PointsEarned: pointsEarned,
|
|
ReceiverName: address.ReceiverName,
|
|
ReceiverPhone: address.Phone,
|
|
ReceiverAddr: fmt.Sprintf("%s%s%s%s", address.Province, address.City, address.District, address.DetailAddr),
|
|
Remark: req.Remark,
|
|
}
|
|
|
|
err = l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
|
|
// 创建订单
|
|
if err := tx.Create(&order).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// 创建订单项
|
|
for i := range orderItems {
|
|
orderItems[i].OrderID = uint(order.ID)
|
|
}
|
|
if err := tx.Create(&orderItems).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// 扣减库存
|
|
for _, cartItem := range cartItems {
|
|
if cartItem.SkuID > 0 {
|
|
if err := tx.Model(&model.ProductSku{}).Where("id = ?", cartItem.SkuID).
|
|
Update("stock", gorm.Expr("stock - ?", cartItem.Quantity)).Error; err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if err := tx.Model(&model.Product{}).Where("id = ?", cartItem.ProductID).
|
|
Update("stock", gorm.Expr("stock - ?", cartItem.Quantity)).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
// 删除购物车项
|
|
if err := tx.Where("id IN ?", req.CartItemIDs).Delete(&model.CartItem{}).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// 扣除积分
|
|
if pointsUsed > 0 {
|
|
if err := tx.Model(&user).Update("points", gorm.Expr("points - ?", pointsUsed)).Error; err != nil {
|
|
return err
|
|
}
|
|
// 记录积分变动
|
|
tx.Create(&model.PointsRecord{
|
|
UserID: userID,
|
|
Type: model.PointsTypeSpend,
|
|
Points: -pointsUsed,
|
|
Balance: user.Points - pointsUsed,
|
|
Source: model.PointsSourceOrder,
|
|
ReferenceID: uint(order.ID),
|
|
Remark: fmt.Sprintf("订单 %s 使用积分", orderNo),
|
|
})
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 构建响应
|
|
respItems := make([]types.OrderItem, 0, len(orderItems))
|
|
for _, item := range orderItems {
|
|
respItems = append(respItems, types.OrderItem{
|
|
ID: uint(item.ID),
|
|
ProductID: item.ProductID,
|
|
SkuID: item.SkuID,
|
|
ProductName: item.ProductName,
|
|
SkuName: item.SkuName,
|
|
Image: item.Image,
|
|
Price: item.Price,
|
|
Quantity: item.Quantity,
|
|
TotalAmount: item.TotalAmount,
|
|
})
|
|
}
|
|
|
|
resp = &types.Order{
|
|
ID: uint(order.ID),
|
|
OrderNo: order.OrderNo,
|
|
Status: order.Status,
|
|
TotalAmount: order.TotalAmount,
|
|
DiscountAmount: order.DiscountAmount,
|
|
ShippingFee: order.ShippingFee,
|
|
PayAmount: order.PayAmount,
|
|
PointsUsed: order.PointsUsed,
|
|
PointsEarned: order.PointsEarned,
|
|
ReceiverName: order.ReceiverName,
|
|
ReceiverPhone: order.ReceiverPhone,
|
|
ReceiverAddr: order.ReceiverAddr,
|
|
Remark: order.Remark,
|
|
Items: respItems,
|
|
CreatedAt: order.CreatedAt.Format("2006-01-02 15:04:05"),
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|