411 lines
14 KiB
Go
411 lines
14 KiB
Go
package repositories
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"fusenapi/constants"
|
|
"fusenapi/model/gmodel"
|
|
"fusenapi/utils/basic"
|
|
"fusenapi/utils/format"
|
|
"fusenapi/utils/order"
|
|
"fusenapi/utils/shopping_cart"
|
|
"fusenapi/utils/step_price"
|
|
"math"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/aws/aws-sdk-go/aws/session"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func NewOrder(gormDB *gorm.DB, bLMServiceUrl *string, awsSession *session.Session) Order {
|
|
return &defaultOrder{
|
|
MysqlConn: gormDB,
|
|
}
|
|
}
|
|
|
|
type (
|
|
defaultOrder struct {
|
|
MysqlConn *gorm.DB
|
|
}
|
|
Order interface {
|
|
// 下单
|
|
Create(ctx context.Context, in *CreateReq) (res *CreateRes, err error)
|
|
// 预支付
|
|
// 列表
|
|
// 详情
|
|
}
|
|
|
|
OrderAddress struct {
|
|
Address string `json:"address"` // 详细地址
|
|
Mobile string `json:"mobile"` // 手机
|
|
Name string `json:"name"` // 姓名
|
|
}
|
|
|
|
/* 下单 */
|
|
CreateReq struct {
|
|
ExpectedDeliveryTime time.Time `json:"expected_delivery_time"` // 预计到货时间
|
|
ExchangeRate int64 `json:"exchange_rate"` // 换算汇率(厘)
|
|
CurrentCurrency string `json:"current_currency"` // 当前货币
|
|
OriginalCurrency string `json:"original_currency"` // 原始货币
|
|
UserId int64 `json:"user_id"`
|
|
CartIds []int64 `json:"cart_ids"`
|
|
DeliveryMethod int64 `json:"delivery_method"`
|
|
DeliveryAddress *OrderAddress `json:"delivery_address"` // 收货地址
|
|
}
|
|
CreateRes struct {
|
|
ErrorCode basic.StatusResponse
|
|
OrderSn string
|
|
}
|
|
/* 下单 */
|
|
)
|
|
|
|
// 下单
|
|
func (d *defaultOrder) Create(ctx context.Context, in *CreateReq) (res *CreateRes, err error) {
|
|
var errorCode basic.StatusResponse
|
|
// 订单编号
|
|
var orderSn string = order.GenerateOrderNumber(int(in.DeliveryMethod), int(in.UserId))
|
|
|
|
err = d.MysqlConn.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
// 查询购物车
|
|
var shoppingCartList []*gmodel.RelaFsShoppingCart
|
|
resShoppingCartFind := tx.Preload("ShoppingCartProduct", func(dbPreload *gorm.DB) *gorm.DB {
|
|
return dbPreload.Table(gmodel.NewFsProductModel(tx).TableName()).Preload("CoverResource")
|
|
}).Preload("ShoppingCartProductPriceList").Preload("ShoppingCartProductModel3dList").Preload("ShoppingCartProductModel3dFitting").
|
|
Where("id IN ?", in.CartIds).
|
|
Where("user_id = ?", in.UserId).
|
|
Find(&shoppingCartList)
|
|
err = resShoppingCartFind.Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
shoppingCartListLen := len(shoppingCartList)
|
|
if shoppingCartListLen == 0 {
|
|
errorCode = *basic.CodeErrOrderCreatShoppingCartEmpty
|
|
return errors.New(errorCode.Message)
|
|
}
|
|
if shoppingCartListLen != len(in.CartIds) {
|
|
errorCode = *basic.CodeErrOrderCreatShoppingCartNotMatched
|
|
return errors.New(errorCode.Message)
|
|
}
|
|
|
|
// 订单商品列表
|
|
var orderProductList []gmodel.OrderProduct
|
|
|
|
var shippingFee gmodel.AmountInfo
|
|
// 订单税费总价(厘)
|
|
var shippingFeeTotal int64
|
|
|
|
var tax = gmodel.AmountInfo{}
|
|
// 订单邮费总价(厘)
|
|
var taxTotal int64
|
|
|
|
var discount gmodel.AmountInfo
|
|
// 订单折扣总价(厘)
|
|
var discountTotal int64
|
|
|
|
var subtotal gmodel.AmountInfo
|
|
// 订单商品总价(厘)
|
|
var orderProductTotal int64
|
|
|
|
var total gmodel.AmountInfo
|
|
// 订单总价(厘)
|
|
var orderTotal int64
|
|
|
|
var nowTime = time.Now().UTC()
|
|
|
|
// 收货地址
|
|
var orderAddress *gmodel.OrderAddress
|
|
|
|
// 支付状态
|
|
var payStatus = constants.ORDERPAYSTATUSUNPAIDDEPOSIT
|
|
|
|
// 直邮
|
|
if in.DeliveryMethod == constants.DELIVERYMETHODDIRECTMAIL {
|
|
orderAddress = &gmodel.OrderAddress{
|
|
Mobile: in.DeliveryAddress.Mobile,
|
|
Name: in.DeliveryAddress.Name,
|
|
}
|
|
}
|
|
|
|
for _, shoppingCart := range shoppingCartList {
|
|
// 购物车快照
|
|
var shoppingCartSnapshot shopping_cart.CartSnapshot
|
|
// 购物车商品价格
|
|
var shoppingCartProductPrice *gmodel.FsProductPrice
|
|
// 购物车商品模型
|
|
var shoppingCartProductModel3d *gmodel.FsProductModel3d
|
|
if shoppingCart.Snapshot != nil {
|
|
json.Unmarshal([]byte(*shoppingCart.Snapshot), &shoppingCartSnapshot)
|
|
}
|
|
// 商品异常
|
|
if shoppingCart.ShoppingCartProduct == nil || (shoppingCart.ShoppingCartProduct != nil && *shoppingCart.ShoppingCartProduct.IsShelf == 0) {
|
|
errorCode = *basic.CodeErrOrderCreatProductAbsent
|
|
errorCode.Message = "create order failed, product '" + shoppingCartSnapshot.ProductInfo.ProductName + "'is absent"
|
|
return errors.New(errorCode.Message)
|
|
}
|
|
// 商品价格异常
|
|
if len(shoppingCart.ShoppingCartProductPriceList) == 0 {
|
|
errorCode = *basic.CodeErrOrderCreatProductPriceAbsent
|
|
errorCode.Message = "create order failed, price of product '" + shoppingCartSnapshot.ProductInfo.ProductName + "'is absent"
|
|
return errors.New(errorCode.Message)
|
|
} else {
|
|
var isProductPrice bool
|
|
for _, shoppingCartProductPriceInfo := range shoppingCart.ShoppingCartProductPriceList {
|
|
if shoppingCart.SizeId == shoppingCartProductPriceInfo.SizeId {
|
|
shoppingCartProductPrice = shoppingCartProductPriceInfo
|
|
isProductPrice = true
|
|
break
|
|
}
|
|
}
|
|
if !isProductPrice {
|
|
errorCode = *basic.CodeErrOrderCreatProductPriceAbsent
|
|
errorCode.Message = "create order failed, price of product '" + shoppingCartSnapshot.ProductInfo.ProductName + "'is absent"
|
|
return errors.New(errorCode.Message)
|
|
}
|
|
shoppingCart.ShoppingCartProductPriceList = []*gmodel.FsProductPrice{shoppingCartProductPrice}
|
|
}
|
|
|
|
// 商品模型异常
|
|
if len(shoppingCart.ShoppingCartProductModel3dList) == 0 {
|
|
errorCode = *basic.CodeErrOrderCreatProductAccessoryAbsent
|
|
errorCode.Message = "create order failed, accessoryof product '" + shoppingCartSnapshot.ProductInfo.ProductName + "'is absent"
|
|
return errors.New(errorCode.Message)
|
|
} else {
|
|
var isProductModel bool
|
|
for _, shoppingCartProductModel3dInfo := range shoppingCart.ShoppingCartProductModel3dList {
|
|
if shoppingCart.SizeId == shoppingCartProductModel3dInfo.SizeId {
|
|
shoppingCartProductModel3d = shoppingCartProductModel3dInfo
|
|
isProductModel = true
|
|
break
|
|
}
|
|
}
|
|
if !isProductModel {
|
|
errorCode = *basic.CodeErrOrderCreatProductAccessoryAbsent
|
|
errorCode.Message = "create order failed, accessory of product '" + shoppingCartSnapshot.ProductInfo.ProductName + "'is absent"
|
|
return errors.New(errorCode.Message)
|
|
}
|
|
shoppingCart.ShoppingCartProductModel3dList = []*gmodel.FsProductModel3d{shoppingCartProductModel3d}
|
|
}
|
|
|
|
var stepNum []int
|
|
var stepPrice []int
|
|
if *shoppingCartProductPrice.StepNum == "" {
|
|
errorCode = *basic.CodeErrOrderCreatProductPriceAbsent
|
|
errorCode.Message = "create order failed, step num of product '" + shoppingCartSnapshot.ProductInfo.ProductName + "'is failed"
|
|
return errors.New(errorCode.Message)
|
|
} else {
|
|
json.Unmarshal([]byte(*shoppingCartProductPrice.StepNum), &stepNum)
|
|
}
|
|
if *shoppingCartProductPrice.StepPrice == "" {
|
|
errorCode = *basic.CodeErrOrderCreatProductPriceAbsent
|
|
errorCode.Message = "create order failed, step price of product '" + shoppingCartSnapshot.ProductInfo.ProductName + "'is failed"
|
|
return errors.New(errorCode.Message)
|
|
} else {
|
|
json.Unmarshal([]byte(*shoppingCartProductPrice.StepPrice), &stepPrice)
|
|
}
|
|
|
|
/* 计算价格 */
|
|
|
|
//阶梯数量切片
|
|
stepNum, err := format.StrSlicToIntSlice(strings.Split(*shoppingCartProductPrice.StepNum, ","))
|
|
if err != nil {
|
|
errorCode = *basic.CodeErrOrderCreatProductPriceAbsent
|
|
errorCode.Message = fmt.Sprintf("failed to parse step number:%d_%d", *shoppingCart.ProductId, *shoppingCart.SizeId)
|
|
return errors.New(errorCode.Message)
|
|
}
|
|
lenStepNum := len(stepNum)
|
|
//阶梯价格切片
|
|
stepPrice, err = format.StrSlicToIntSlice(strings.Split(*shoppingCartProductPrice.StepPrice, ","))
|
|
if err != nil {
|
|
errorCode = *basic.CodeErrOrderCreatProductPriceAbsent
|
|
errorCode.Message = fmt.Sprintf("failed to parse step price:%d_%d", *shoppingCart.ProductId, *shoppingCart.SizeId)
|
|
return errors.New(errorCode.Message)
|
|
}
|
|
lenStepPrice := len(stepPrice)
|
|
if lenStepPrice == 0 || lenStepNum == 0 {
|
|
errorCode = *basic.CodeErrOrderCreatProductPriceAbsent
|
|
errorCode.Message = fmt.Sprintf("step price or step number is not set:%d_%d", *shoppingCart.ProductId, *shoppingCart.SizeId)
|
|
return errors.New(errorCode.Message)
|
|
}
|
|
// 购买数量
|
|
reqPurchaseQuantity := *shoppingCart.PurchaseQuantity
|
|
// 购买箱数
|
|
boxQuantity := int(math.Ceil(float64(reqPurchaseQuantity) / float64(*shoppingCartProductPrice.EachBoxNum)))
|
|
// 根据数量获取阶梯价格中对应的价格
|
|
productPrice := step_price.GetCentStepPrice(boxQuantity, stepNum, stepPrice)
|
|
// 如果有配件,单价也要加入配件价格
|
|
if *shoppingCart.FittingId > 0 {
|
|
if shoppingCart.ShoppingCartProductModel3dFitting != nil {
|
|
productPrice = productPrice + *shoppingCart.ShoppingCartProductModel3dFitting.Price
|
|
}
|
|
}
|
|
// 单个购物车总价(厘)
|
|
productTotalPrice := productPrice * reqPurchaseQuantity
|
|
|
|
/* 计算价格 */
|
|
|
|
// 订单商品总价(厘)
|
|
orderProductTotal = orderProductTotal + productTotalPrice
|
|
|
|
// 订单商品
|
|
var productSnapshot = make(map[string]interface{}, 1)
|
|
productSnapshot["product_snapshot"] = shoppingCart.ShoppingCartProduct
|
|
var productCoverMetadata map[string]interface{}
|
|
if shoppingCart.ShoppingCartProduct.CoverResource != nil && shoppingCart.ShoppingCartProduct.CoverResource.Metadata != nil {
|
|
json.Unmarshal(*shoppingCart.ShoppingCartProduct.CoverResource.Metadata, &productCoverMetadata)
|
|
}
|
|
orderProductList = append(orderProductList, gmodel.OrderProduct{
|
|
TotalPrice: order.GetAmountInfo(order.GetAmountInfoReq{
|
|
ExchangeRate: in.ExchangeRate,
|
|
Initiate: productTotalPrice,
|
|
Current: productTotalPrice,
|
|
CurrentCurrency: in.CurrentCurrency,
|
|
OriginalCurrency: in.OriginalCurrency,
|
|
}),
|
|
ExpectedDeliveryTime: in.ExpectedDeliveryTime,
|
|
PurchaseQuantity: *shoppingCart.PurchaseQuantity,
|
|
ProductID: *shoppingCart.ProductId,
|
|
ProductCover: *shoppingCart.ShoppingCartProduct.Cover,
|
|
ProductCoverMetadata: productCoverMetadata,
|
|
ProductName: *shoppingCart.ShoppingCartProduct.Title,
|
|
ItemPrice: order.GetAmountInfo(order.GetAmountInfoReq{
|
|
ExchangeRate: in.ExchangeRate,
|
|
Initiate: productPrice,
|
|
Current: productPrice,
|
|
CurrentCurrency: in.CurrentCurrency,
|
|
OriginalCurrency: in.OriginalCurrency,
|
|
}),
|
|
ProductSnapshot: productSnapshot,
|
|
ShoppingCartSnapshot: &shoppingCart.FsShoppingCart,
|
|
ProductSn: *shoppingCart.ShoppingCartProduct.Sn,
|
|
DiyInformation: &gmodel.DiyInformation{
|
|
Address: shoppingCartSnapshot.UserDiyInformation.Address,
|
|
Phone: shoppingCartSnapshot.UserDiyInformation.Phone,
|
|
Qrcode: shoppingCartSnapshot.UserDiyInformation.Qrcode,
|
|
Slogan: shoppingCartSnapshot.UserDiyInformation.Slogan,
|
|
Website: shoppingCartSnapshot.UserDiyInformation.Website,
|
|
},
|
|
FittingInfo: &gmodel.FittingInfo{
|
|
FittingID: *shoppingCart.FittingId,
|
|
FittingName: shoppingCartSnapshot.FittingInfo.FittingName,
|
|
},
|
|
SizeInfo: &gmodel.SizeInfo{
|
|
SizeID: *shoppingCart.SizeId,
|
|
Capacity: shoppingCartSnapshot.SizeInfo.Capacity,
|
|
},
|
|
StepNum: stepNum,
|
|
})
|
|
}
|
|
|
|
subtotal = order.GetAmountInfo(order.GetAmountInfoReq{
|
|
ExchangeRate: in.ExchangeRate,
|
|
Initiate: orderProductTotal,
|
|
Current: orderProductTotal,
|
|
CurrentCurrency: in.CurrentCurrency,
|
|
OriginalCurrency: in.OriginalCurrency,
|
|
})
|
|
|
|
orderTotal = orderProductTotal + shippingFeeTotal + taxTotal - discountTotal
|
|
total = order.GetAmountInfo(order.GetAmountInfoReq{
|
|
ExchangeRate: in.ExchangeRate,
|
|
Initiate: orderTotal,
|
|
Current: orderTotal,
|
|
CurrentCurrency: in.CurrentCurrency,
|
|
OriginalCurrency: in.OriginalCurrency,
|
|
})
|
|
// 定金
|
|
var depositInt int64 = orderTotal / 2
|
|
var deposit = gmodel.PayInfo{
|
|
Status: gmodel.PayStatus{
|
|
StatusCode: int64(constants.PAYSTATUSUNPAID),
|
|
StatusTitle: constants.PayStatusMessage[constants.PAYSTATUSUNPAID],
|
|
},
|
|
PayAmount: order.GetAmountInfo(order.GetAmountInfoReq{
|
|
ExchangeRate: in.ExchangeRate,
|
|
Initiate: depositInt,
|
|
Current: depositInt,
|
|
CurrentCurrency: in.CurrentCurrency,
|
|
OriginalCurrency: in.OriginalCurrency,
|
|
}),
|
|
}
|
|
|
|
// 尾款
|
|
var remainingBalanceInt int64 = orderTotal - depositInt
|
|
var remainingBalance = gmodel.PayInfo{
|
|
Status: gmodel.PayStatus{
|
|
StatusCode: int64(constants.PAYSTATUSUNPAID),
|
|
StatusTitle: constants.PayStatusMessage[constants.PAYSTATUSUNPAID],
|
|
},
|
|
PayAmount: order.GetAmountInfo(order.GetAmountInfoReq{
|
|
ExchangeRate: in.ExchangeRate,
|
|
Initiate: remainingBalanceInt,
|
|
Current: remainingBalanceInt,
|
|
CurrentCurrency: in.CurrentCurrency,
|
|
OriginalCurrency: in.OriginalCurrency,
|
|
}),
|
|
}
|
|
var orderAmount = gmodel.OrderAmount{
|
|
Deposit: deposit,
|
|
RemainingBalance: remainingBalance,
|
|
Discount: discount,
|
|
ShippingFee: shippingFee,
|
|
Tax: tax,
|
|
Subtotal: subtotal,
|
|
Total: total,
|
|
}
|
|
|
|
// 订单状态--当前
|
|
var status = gmodel.OrderStatus{
|
|
Ctime: nowTime,
|
|
Utime: nowTime,
|
|
StatusCode: constants.ORDERSTATUSUNPAIDDEPOSIT,
|
|
StatusTitle: constants.OrderStatusMessage[constants.ORDERSTATUSUNPAIDDEPOSIT],
|
|
}
|
|
// 订单状态--链路
|
|
var statusLink = order.GenerateOrderStatusLink(in.DeliveryMethod, nowTime, in.ExpectedDeliveryTime)
|
|
var orderInfo = gmodel.OrderInfo{
|
|
Ctime: nowTime,
|
|
DeliveryMethod: in.DeliveryMethod,
|
|
OrderSn: orderSn,
|
|
Status: status,
|
|
StatusLink: statusLink,
|
|
}
|
|
|
|
var orderDetail = gmodel.OrderDetail{
|
|
DeliveryAddress: orderAddress,
|
|
OrderAmount: orderAmount,
|
|
OrderInfo: orderInfo,
|
|
OrderProduct: orderProductList,
|
|
PayStatus: payStatus,
|
|
}
|
|
// 数据库操作
|
|
var order = gmodel.NewFsOrder{
|
|
UserId: &in.UserId,
|
|
DeliveryMethod: &in.DeliveryMethod,
|
|
OrderSn: &orderSn,
|
|
Status: (*int64)(&status.StatusCode),
|
|
PayStatus: (*int64)(&payStatus),
|
|
Ctime: &nowTime,
|
|
Metadata: &orderDetail,
|
|
}
|
|
result := tx.Create(&order)
|
|
if result.Error != nil {
|
|
return result.Error
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return &CreateRes{
|
|
OrderSn: orderSn,
|
|
ErrorCode: errorCode,
|
|
}, err
|
|
}
|
|
return &CreateRes{
|
|
OrderSn: orderSn,
|
|
ErrorCode: errorCode,
|
|
}, nil
|
|
}
|