package order_preview import ( "math" "recook/internal/back" "recook/internal/dbc" "recook/internal/model/freight" "recook/internal/model/goods" "recook/internal/model/order_preview" "recook/internal/model/promotion" "recook/internal/model/user" "recook/internal/model/vend" "recook/internal/service/baseCode" "recook/internal/v2/lib/jcook" "recook/internal/v2/model/flashsale" goods3 "recook/internal/v2/model/recook/goods" "recook/tools" "strconv" "time" "github.com/gin-gonic/gin" "github.com/jinzhu/gorm" "github.com/shopspring/decimal" ) /*普通订单预览的参数*/ type previewNormalOrderParam struct { UserID uint `json:"userId" validate:"required"` SkuID uint `json:"skuId" validate:"required"` Quantity uint `json:"quantity" validate:"required"` ParentID uint `json:"parentId"` // 可选字段 如果是链接分享 则传入该字段,代表分享者的id。 没有的话。就是上级的id,顶级用户没有 UseCoin int `json:"useCoin"` //默认0 开启 1 关闭 LiveId uint `json:"liveId"` //直播间id Address string `json:"address"` InvitationNo string `json:"invitation_no"` } // CreatePreviewNormalOrder 创建普通订单,从商品详情页直接点击《立即购买》按钮进入购买页面,客户端不计算价格,服务端进行计算。 // 明天参加活动的商品不允许购买 func CreatePreviewNormalOrder(c *gin.Context) { var p previewNormalOrderParam err := tools.ParseParams(&p, c) if err != nil { back.Fail(c, err.Error()) return } if p.UserID <= 0 { back.Fail(c, "游客无法使用该功能,请先登录") return } if p.Quantity > 50 { back.Fail(c, "单件最多拍50件") return } var ancestorID uint = 0 var myInfo user.Information if err := dbc.DB.First(&myInfo, p.UserID).Error; err != nil { back.Err(c, err.Error()) return } ancestorID = myInfo.AncestorID sharerId := myInfo.ID if p.InvitationNo != "" { sharerId = uint(baseCode.Decode(p.InvitationNo)) } if sharerId == 0 { sharerId = myInfo.ID } preOrderInfo := &order_preview.Information{} preOrderAddr := &order_preview.Addr{} preOrderGoods := &order_preview.GoodsDetail{} actualAmount := decimal.NewFromFloat(0.0) unitPrice := decimal.NewFromFloat(0.0) // 单价 expressFee := decimal.NewFromFloat(0.0) // 快递费 commission := decimal.NewFromInt(0) brandCouponReducePrice := decimal.NewFromFloat(0.0) // 品牌优惠券抵扣的价格 universeCouponReducePrice := decimal.NewFromFloat(0.0) // 通用优惠券抵扣的价格 coinReducePrice := decimal.NewFromFloat(0.0) // 使用瑞币的价格 mainPhotoURL := "" // 首先检测库存 var sku goods.Sku cost := decimal.Zero { err = dbc.DB.First(&sku, "id = ?", p.SkuID).Error if err != nil { back.Err(c, err.Error()) return } cost = cost.Add(cost.Add(sku.PurchasePrice.Mul(decimal.NewFromInt(int64(p.Quantity))))) } var promotionGoods promotion.Goods var promotionSku promotion.Sku dbc.DB.First(&promotionGoods, "goods_id = ? AND start_time <= ? AND end_time > ?", sku.GoodsID, time.Now(), time.Now()) if promotionGoods.ID == 0 { dbc.DB.First(&promotionGoods, "goods_id = ? AND start_time > ?", sku.GoodsID, time.Now()) } // 仅考虑商品的库存 dbc.DB.First(&promotionSku, "promotion_goods_id = ? AND sku_id = ?", promotionGoods.ID, p.SkuID) if p.Quantity > sku.Inventory { back.Fail(c, "库存不足") return } unitPrice = sku.DiscountPrice commission = sku.Commission mainPhotoURL = sku.PicURL if len(sku.PicURL) == 0 { // 使用主图 var mainPhoto goods.MainPhoto err := dbc.DB.First(&mainPhoto, "goods_id = ? AND is_master = 1", sku.GoodsID).Error if err != nil { back.Fail(c, err.Error()) return } mainPhotoURL = mainPhoto.URL } if unitPrice.LessThan(decimal.NewFromFloat(0.01)) { back.Err(c, "当前商品价格太低。内部错误") return } var defaultAddr user.Addr { err = dbc.DB.First(&defaultAddr, "user_id = ? AND is_default = 1", p.UserID).Error if err != nil && false == gorm.IsRecordNotFoundError(err) { back.Err(c, err.Error()) return } } if sku.ThirdPartyType == 3 && p.Address == "" && defaultAddr.ID == 0 { back.Err(c, "请填写默认收货地址") return } var goodsInfo goods.Information { err = dbc.DB.First(&goodsInfo, "id = ?", sku.GoodsID).Error if err != nil { back.Err(c, err.Error()) return } if goodsInfo.PublishStatus == 0 { back.Fail(c, "商品已下架:"+goodsInfo.GoodsName) return } } if (sku.ThirdPartyType == 3 && defaultAddr.ID > 0) || p.Address != "" { // 京东商品查询物流信息 client := jcook.GetClient() if len(p.Address) == 0 { p.Address = defaultAddr.GetAddress() } id, _ := strconv.Atoi(sku.ThirdPartySkuId) req := jcook.SkuStockReq{ Address: p.Address, SkuList: []jcook.SkuQuantity{ { uint(id), 1, }, }, } var resp []jcook.SkuStockResp if err = client.Exec(req, &resp); err != nil { back.Err(c, err.Error()) return } for _, v := range resp { if v.StockState == 0 { back.Err(c, "该地区该商品无货") return } } //var sku goods3.RecookGoodsSkuModel //mysql.Db.First(&sku, "goods_id = ?", goodsInfo.ID) req1 := jcook.LogisticsFeeReq{ Address: p.Address, SkuList: []jcook.LogisticsInfo{ { uint(id), p.Quantity, sku.PurchasePrice, }, }, OrderFee: sku.PurchasePrice.Mul(decimal.NewFromInt(int64(p.Quantity))), } var resp2 jcook.LogisticsFeeResp if err := client.Exec(req1, &resp2); err != nil { back.Err(c, err.Error()) return } expressFee = resp2.Fee preOrderAddr.IsDeliveryArea = 1 } else if defaultAddr.ID > 0 { canDelivery, expFee, err := computeExpressFeeWithGoods(p.Quantity, &goodsInfo, defaultAddr.Province, defaultAddr.City) if err != nil { back.Err(c, err.Error()) return } if canDelivery { expressFee = expFee preOrderAddr.IsDeliveryArea = 1 } } var brandInfo goods.Brand { err = dbc.DB.First(&brandInfo, "id = ?", goodsInfo.BrandID).Error if err != nil { back.Err(c, err.Error()) return } } var firCate goods.Category var secCate goods.Category dbc.DB.Select("name").First(&firCate, goodsInfo.FirstCategoryID) dbc.DB.Select("name").First(&secCate, goodsInfo.SecondCategoryID) vendorName := "自营" var vendor vend.Information { if goodsInfo.VendorID > 0 { err = dbc.DB.Select("id, username").First(&vendor, "id = ?", goodsInfo.VendorID).Error if err != nil { back.Err(c, err.Error()) return } vendorName = vendor.Username } } actualAmount = unitPrice.Mul(decimal.NewFromInt(int64(p.Quantity))).Add(expressFee) var wallet user.Wallet { err = dbc.DB.First(&wallet, "user_id = ?", p.UserID).Error if err != nil { back.Err(c, err.Error()) return } if p.UseCoin < 1 && (&goods3.RecookGoodsInfoModel{}).HasCoin(goodsInfo.Storehouse) { //coinAmount := decimal.NewFromInt(int64(wallet.Coin)).Div(decimal.NewFromInt(100)) // 将瑞币折算为实际金额 coinAmount := wallet.Coin // 瑞币折算按照 1:1 if actualAmount.GreaterThanOrEqual(coinAmount) { // 保证不过量抵扣 actualAmount = actualAmount.Sub(coinAmount) coinReducePrice = coinAmount } else { // 瑞比足够支付时 将瑞币直接使用掉 订单金额为0 coinReducePrice = actualAmount actualAmount = decimal.NewFromFloat(0.0) } } } tx := dbc.DB.Begin() { { preOrderInfo.AncestorID = ancestorID preOrderInfo.ParentID = myInfo.ParentID preOrderInfo.SharerID = sharerId preOrderInfo.LiveId = p.LiveId preOrderInfo.UserID = p.UserID preOrderInfo.Title = goodsInfo.GoodsName preOrderInfo.BrandCouponTotalAmount = brandCouponReducePrice preOrderInfo.UniverseCouponTotalAmount = universeCouponReducePrice preOrderInfo.CoinTotalAmount = coinReducePrice preOrderInfo.ExpressTotalFee = expressFee preOrderInfo.GoodsTotalAmount = unitPrice.Mul(decimal.NewFromInt(int64(p.Quantity))) preOrderInfo.GoodsTotalCommission = commission.Mul(decimal.NewFromInt(int64(p.Quantity))) preOrderInfo.ActualTotalAmount = actualAmount preOrderInfo.Cost = cost err = tx.Create(&preOrderInfo).Error if err != nil { back.Err(c, err.Error()) tx.Rollback() return } } // 地址 { preOrderAddr.OrderID = preOrderInfo.ID preOrderAddr.Province = defaultAddr.Province preOrderAddr.City = defaultAddr.City preOrderAddr.District = defaultAddr.District preOrderAddr.ReceiverName = defaultAddr.Name preOrderAddr.Mobile = defaultAddr.Mobile preOrderAddr.AddressID = defaultAddr.ID preOrderAddr.Address = defaultAddr.Address err = tx.Create(&preOrderAddr).Error if err != nil { back.Err(c, err.Error()) tx.Rollback() return } } { preOrderGoods.OrderID = preOrderInfo.ID preOrderGoods.VendorID = goodsInfo.VendorID preOrderGoods.VendorName = vendorName preOrderGoods.BrandID = brandInfo.ID preOrderGoods.BrandName = brandInfo.Name preOrderGoods.CategoryName = firCate.Name + "/" + secCate.Name preOrderGoods.GoodsID = goodsInfo.ID preOrderGoods.GoodsName = goodsInfo.GoodsName preOrderGoods.IsJoinTeamPerformance = goodsInfo.IsJoinTeamPerformance preOrderGoods.Hash = goodsInfo.Hash preOrderGoods.SkuID = sku.ID preOrderGoods.SkuName = sku.Name preOrderGoods.SkuCode = sku.Code preOrderGoods.MainPhotoURL = mainPhotoURL preOrderGoods.Quantity = p.Quantity preOrderGoods.FreightID = goodsInfo.FreightID preOrderGoods.Weight = goodsInfo.Weight preOrderGoods.PromotionSkuId = promotionSku.ID preOrderGoods.PromotionGoodsId = promotionGoods.ID preOrderGoods.PromotionName = promotionGoods.PromotionName preOrderGoods.PromotionStartTime = promotionGoods.StartTime preOrderGoods.PromotionEndTime = promotionGoods.EndTime preOrderGoods.UnitPrice = unitPrice preOrderGoods.PurchasePrice = sku.PurchasePrice preOrderGoods.TotalCommission = preOrderInfo.GoodsTotalCommission preOrderGoods.BrandCouponAmount = brandCouponReducePrice preOrderGoods.UniverseCouponAmount = universeCouponReducePrice preOrderGoods.CoinAmount = coinReducePrice preOrderGoods.GoodsAmount = unitPrice.Mul(decimal.NewFromInt(int64(p.Quantity))) preOrderGoods.ExpressFee = expressFee preOrderGoods.ActualAmount = actualAmount preOrderGoods.IsImport = goodsInfo.IsImport preOrderGoods.Storehouse = goodsInfo.Storehouse preOrderGoods.IsFerme = goodsInfo.IsFerme err = tx.Create(&preOrderGoods).Error if err != nil { back.Err(c, err.Error()) tx.Rollback() return } } } tx.Commit() back.Suc(c, "操作成功", queryPreviewOrderGroup(preOrderInfo.ID)) } func computeExpressFeeWithGoods(quantity uint, goodsInfo *goods.Information, province, city string) (bool, decimal.Decimal, error) { var freightTemplateInfo freight.Information // 只有用户有默认地址才去计算运费价格 err := dbc.DB.First(&freightTemplateInfo, "id = ?", goodsInfo.FreightID).Error if err != nil { return false, decimal.NewFromFloat(0.0), err } // 检测是否属于不发货地区 // 不发货的省 var nonDeliveryProvince freight.NoProvince err = dbc.DB.First(&nonDeliveryProvince, "freight_id = ? AND province_name = ?", goodsInfo.FreightID, province).Error if err != nil && false == gorm.IsRecordNotFoundError(err) { return false, decimal.Zero, err } if nonDeliveryProvince.ID > 0 { return false, decimal.NewFromFloat(0.0), nil } // 不发货的市 var noCitys freight.NoCitys err = dbc.DB.First(&noCitys, "freight_id = ? AND province_name = ? and city_name = ?", goodsInfo.FreightID, province, city).Error if err != nil && gorm.IsRecordNotFoundError(err) == false { return false, decimal.Zero, err } if noCitys.ID > 0 { return false, decimal.NewFromFloat(0.0), nil } // 进入该分支代表该地址可以发货 需要查询运费信息 // 先判断是不是所有地方包邮 var freightInfo freight.Information err = dbc.DB.First(&freightInfo, "id = ?", goodsInfo.FreightID).Error if err != nil { return false, decimal.NewFromFloat(0.0), nil } if freightInfo.AllFree == 1 { return true, decimal.NewFromFloat(0.0), nil } // 先找到该收货省市的信息 然后找到对应的价格表 var priceDetailID uint // 市价格表 var deliveryCity freight.Citys err = dbc.DB.First(&deliveryCity, "freight_id = ? AND province_name = ? and city_name = ?", goodsInfo.FreightID, province, city).Error if deliveryCity.ID == 0 { // 省价格表 var deliveryProvince freight.Province err = dbc.DB.First(&deliveryProvince, "freight_id = ? AND province_name = ?", goodsInfo.FreightID, province).Error if err != nil { return true, decimal.Zero, nil } priceDetailID = deliveryProvince.PriceDetailID } else { priceDetailID = deliveryCity.PriceDetailID } var freightPrice freight.Price dbc.DB.First(&freightPrice, "id = ?", priceDetailID) expressFee := decimal.NewFromFloat(0.0) if freightPrice.Type == 0 { // 按照件数计算 if quantity <= freightPrice.FirstNumber { expressFee = freightPrice.FirstNumberFee } else { baseNumber := math.Ceil(float64((quantity - freightPrice.FirstNumber) / freightPrice.AdditionalNumber)) // 向上取整 意思是不满一斤算一斤 expressFee = freightPrice.FirstNumberFee.Add(decimal.NewFromFloat(baseNumber).Mul(freightPrice.AdditionalNumberFee)) } } else if freightPrice.Type == 3 { // 固定计费 return true, freightPrice.Amount, nil //if goodsInfo.Weight.LessThanOrEqual(freightPrice.FirstWeight) { // 小于首重 // expressFee = freightPrice.FirstWeightFee //} else { // baseWeight := (goodsInfo.Weight.Sub(freightPrice.FirstWeight)).Div(freightPrice.AdditionalWeight).Ceil() // 向上取整 意思是不满一斤算一斤 // expressFee = freightPrice.FirstWeightFee.Sub(baseWeight.Mul(freightPrice.AdditionalWeightFee)) //} } else { // 按照重量计算 if goodsInfo.Weight.LessThanOrEqual(freightPrice.FirstWeight) { // 小于首重 expressFee = freightPrice.FirstWeightFee } else { baseWeight := (goodsInfo.Weight.Sub(freightPrice.FirstWeight)).Div(freightPrice.AdditionalWeight).Ceil() // 向上取整 意思是不满一斤算一斤 expressFee = freightPrice.FirstWeightFee.Sub(baseWeight.Mul(freightPrice.AdditionalWeightFee)) } } if quantity >= freightPrice.FreeNumberThreshold { expressFee = decimal.NewFromFloat(0.0) } return true, expressFee, nil } func IfSecKill(now time.Time, skuID uint) (flashsale.RecookSecKillGoodsModel, flashsale.RecookSecKillSortModel) { var seckill flashsale.RecookSecKillModel dbc.DB.Table(seckill.TableName()).Where("activity_start_time?", now).Where("status=2").First(&seckill) //获取当前时间段的商品 now2 := time.Date(0, 1, 1, now.Hour(), now.Minute(), now.Second(), 0, time.Local) var secgoods flashsale.RecookSecKillSortModel dbc.DB.Table((&flashsale.RecookSecKillSortModel{}).TableName()).Where("show_time_start?", now2).Where("sec_kill_activity_id=?", seckill.Id).Where("sku_id=?", skuID).First(&secgoods) var gs flashsale.RecookSecKillGoodsModel dbc.DB.Table(gs.TableName()).Where("sec_kill_activity_id=?", seckill.Id).Where("goods_sku_id=?", secgoods.SkuId).First(&gs) return gs, secgoods }