package repositories import ( "context" "encoding/json" "errors" "fusenapi/constants" "fusenapi/model/gmodel" "fusenapi/utils/basic" "fusenapi/utils/order" "time" "github.com/aws/aws-sdk-go/aws/session" "github.com/zeromicro/go-zero/core/logx" "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) // 预支付 // 列表 // 详情 Detail(ctx context.Context, in *DetailReq) (res *DetailRes, 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 } /* 下单 */ /* 详情 */ DetailReq struct { UserId int64 `json:"user_id"` OrderSn string `json:"order_sn"` } DetailRes struct { OrderDetail gmodel.OrderDetail } /* 详情 */ ) // 详情 func (d *defaultOrder) Detail(ctx context.Context, in *DetailReq) (res *DetailRes, err error) { var order gmodel.FsOrder result := d.MysqlConn.Where("order_sn = ?", in.OrderSn).Where("user_id = ?", in.UserId).Take(&order) if result.Error != nil { logx.Errorf("detail failed, err: %v", err) return nil, result.Error } ress, err := d.OrderDetailHandler(ctx, &order) if err != nil { return nil, err } return &DetailRes{ ress.OrderDetail, }, nil } func (d *defaultOrder) OrderDetailHandler(ctx context.Context, orderInfo *gmodel.FsOrder) (res *DetailRes, err error) { var orderDetail gmodel.OrderDetail err = json.Unmarshal(*orderInfo.Metadata, &orderDetail) if err != nil { logx.Errorf("detail handler unmarshal metadata failed, err: %v", err) return nil, err } for orderProductKey, orderProduct := range orderDetail.OrderProduct { orderDetail.OrderProduct[orderProductKey].TotalPrice = order.GetAmountInfoFormat(&orderProduct.TotalPrice) orderDetail.OrderProduct[orderProductKey].ItemPrice = order.GetAmountInfoFormat(&orderProduct.ItemPrice) orderDetail.OrderProduct[orderProductKey].ShoppingCartSnapshot = nil orderDetail.OrderProduct[orderProductKey].ProductSnapshot = nil } orderDetail.OrderInfo.StatusLink = order.GetOrderStatusLinkUser(orderDetail.OrderInfo.DeliveryMethod, orderDetail.OrderInfo.StatusLink) orderDetail.OrderAmount.Deposit.PayAmount = order.GetAmountInfoFormat(&orderDetail.OrderAmount.Deposit.PayAmount) orderDetail.OrderAmount.RemainingBalance.PayAmount = order.GetAmountInfoFormat(&orderDetail.OrderAmount.RemainingBalance.PayAmount) orderDetail.OrderAmount.Subtotal = order.GetAmountInfoFormat(&orderDetail.OrderAmount.Subtotal) orderDetail.OrderAmount.Total = order.GetAmountInfoFormat(&orderDetail.OrderAmount.Total) return &DetailRes{ OrderDetail: orderDetail, }, nil } // 下单 func (d *defaultOrder) Create(ctx context.Context, in *CreateReq) (res *CreateRes, err error) { var errorCode basic.StatusResponse // 订单编号 var orderSn string = order.GenerateOrderNumber() err = d.MysqlConn.WithContext(ctx).Transaction(func(tx *gorm.DB) error { // 查询购物车 var shoppingCartList []*gmodel.RelaFsShoppingCart resShoppingCartFind := tx.Table(gmodel.NewFsShoppingCartModel(tx).TableName()). 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 gmodel.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) } /* 计算价格 */ productPrice, productTotalPrice, stepNum, stepPrice, err := NewShoppingCart(tx, nil, nil).CaculateCartPrice(*shoppingCart.PurchaseQuantity, shoppingCartProductPrice, *shoppingCart.ShoppingCartProductModel3dFitting.Price) if err != nil { errorCode = *basic.CodeErrOrderCreatProductPriceAbsent errorCode.Message = "create order failed, step price of product '" + shoppingCartSnapshot.ProductInfo.ProductName + "'is failed" return err } /* 计算价格 */ // 订单商品总价(厘) orderProductTotal = orderProductTotal + productTotalPrice // 订单商品 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: shoppingCart.ShoppingCartProduct, ShoppingCartSnapshot: &shoppingCart.FsShoppingCart, ProductSn: *shoppingCart.ShoppingCartProduct.Sn, DiyInformation: &shoppingCartSnapshot.UserDiyInformation, FittingInfo: &gmodel.OrderProductFittingInfo{ FittingID: *shoppingCart.FittingId, FittingName: shoppingCartSnapshot.FittingInfo.FittingName, }, SizeInfo: &gmodel.OrderProductSizeInfo{ SizeID: *shoppingCart.SizeId, Capacity: shoppingCartSnapshot.SizeInfo.Capacity, Title: gmodel.OrderProductSizeInfoTitle{ Inch: shoppingCartSnapshot.SizeInfo.Inch, Cm: shoppingCartSnapshot.SizeInfo.Cm, }, }, StepNum: stepNum, IsHighlyCustomized: *shoppingCart.IsHighlyCustomized, }) } 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, } // 数据库操作 orderDetailByte, err := json.Marshal(orderDetail) if err != nil { return err } var order = gmodel.FsOrder{ UserId: &in.UserId, DeliveryMethod: &in.DeliveryMethod, OrderSn: &orderSn, Status: (*int64)(&status.StatusCode), PayStatus: (*int64)(&payStatus), Ctime: &nowTime, Metadata: &orderDetailByte, } result := tx.Create(&order) if result.Error != nil { return result.Error } return nil }) if err != nil { logx.Errorf("create order failed, err: %v", err) if errorCode.Code == 0 { errorCode.Code = basic.CodeApiErr.Code errorCode.Message = basic.CodeApiErr.Message } return &CreateRes{ OrderSn: orderSn, ErrorCode: errorCode, }, err } return &CreateRes{ OrderSn: orderSn, ErrorCode: errorCode, }, nil }