package order_preview import ( "errors" "fmt" "git.oa00.com/go/mysql" "github.com/gin-gonic/gin" "github.com/jinzhu/gorm" "github.com/shopspring/decimal" "log" "math" "recook/configs" "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/v2/lib/jcook" "recook/internal/v2/lib/shama" "recook/internal/v2/lib/supply" "recook/internal/v2/model/flashsale" goods2 "recook/internal/v2/model/recook/goods" "recook/tools" "strconv" "time" ) /*普通订单预览的参数*/ 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:"invite"` } // 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 != "" { var shareUser user.Information dbc.DB.First(&shareUser, "invitation_no=?", p.InvitationNo) // sharerId = uint(baseCode.Decode(p.InvitationNo)) sharerId = shareUser.ID } 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 var gs goods.Information { err = dbc.DB.First(&sku, "id = ?", p.SkuID).Error if err != nil { back.Err(c, err.Error()) return } err = dbc.DB.First(&gs, "id = ?", sku.GoodsID).Error if err != nil { back.Err(c, err.Error()) return } if gs.IsVipGoods() { if myInfo.Level == 10 { err = errors.New("合伙人无需购买VIP权益卡") back.Err(c, err.Error()) return } } q := decimal.NewFromInt(int64(p.Quantity)) cost = cost.Add(cost.Add(sku.PurchasePrice.Mul(q))) if !gs.IsVipGoods() { coinReducePrice = coinReducePrice.Add(sku.GetSelfProfit(myInfo.Level).Mul(q)) } if sku.ThirdPartyType != 0 && !configs.IsProductionEnv() && sku.ThirdPartyType != 5 { back.Fail(c, "测试服不支持购买") return } } 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 dbc.DB.First(&mainPhoto, "goods_id = ? AND is_master = 1", sku.GoodsID) mainPhotoURL = mainPhoto.URL } if unitPrice.LessThan(decimal.NewFromFloat(0.01)) { back.Err(c, "当前商品价格太低。内部错误") return } var defaultAddr user.Addr { if !gs.IsVipGoods() { err = dbc.DB.First(&defaultAddr, "user_id = ? AND is_default = 1", p.UserID).Error if err != nil && !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 != 0 && defaultAddr.ID > 0) || p.Address != "" { // 京东商品查询物流信息 switch sku.ThirdPartyType { case 3: 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{ { SkuID: uint(id), Quantity: 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 } } req1 := jcook.LogisticsFeeReq{ Address: p.Address, SkuList: []jcook.LogisticsInfo{ { SkuID: uint(id), Quantity: p.Quantity, SkuPrice: sku.PurchasePrice, }, }, OrderFee: sku.PurchasePrice.Mul(decimal.NewFromInt(int64(p.Quantity))), } var resp2 jcook.LogisticsFeeResp if err := client.Exec(req1, &resp2); err != nil { if err.Error() == "商品价格不匹配" { go func() { rq := jcook.SkuPriceReq{ SkuIDSet: []uint{uint(id)}, } var res []jcook.SkuPriceResp if err := client.Exec(rq, &res); err != nil { return } if len(res) == 0 { return } t := res[0] var sku goods2.RecookGoodsSkuModel if err := mysql.Db.Table(sku.TableName()).Where("third_party_sku_id = ? and third_party_type=3", strconv.FormatUint(uint64(t.SkuID), 10)).Updates(map[string]interface{}{ "purchase_price": t.SupplyPrice, }).Error; err != nil { log.Println(err.Error()) return } }() back.Err(c, "价格变动,请刷新后重试") return } back.Err(c, err.Error()) return } expressFee = resp2.Fee preOrderAddr.IsDeliveryArea = 1 case 4: client := shama.GetClient() if len(p.Address) == 0 { p.Address = defaultAddr.GetAddress() } id, _ := strconv.Atoi(sku.ThirdPartySkuId) req := shama.SkuStockReq{ Address: p.Address, SkuList: []shama.SkuQuantity{ { SkuID: uint(id), Quantity: 1, }, }, } var resp []shama.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 } } req1 := shama.LogisticsFeeReq{ Address: p.Address, SkuList: []shama.LogisticsInfo{ { SkuID: uint(id), Quantity: p.Quantity, SkuPrice: sku.PurchasePrice, }, }, OrderFee: sku.PurchasePrice.Mul(decimal.NewFromInt(int64(p.Quantity))), } var resp2 shama.LogisticsFeeResp if err := client.Exec(req1, &resp2); err != nil { back.Err(c, err.Error()) return } expressFee = resp2.Fee preOrderAddr.IsDeliveryArea = 1 case 5: // 供应链接口 if len(p.Address) == 0 { p.Address = defaultAddr.GetAddress() } id, _ := strconv.Atoi(sku.ThirdPartySkuId) stock, err := supply.Api.Sku.Stock(p.Address, []supply.SkuStockItem{{ SkuId: uint(id), Quantity: 1, }}) if err != nil { back.Err(c, err.Error()) return } for _, skuStock := range stock { if skuStock.State == supply.SkuStateOut { back.Err(c, "该地区该商品无货") return } } freightFees, err := supply.Api.Order.FreightFee(p.Address, []supply.OrderFreightFeeItem{{ SkuId: uint(id), Quantity: 1, Price: sku.PurchasePrice, }}) if err != nil { back.Err(c, err.Error()) return } log.Println(freightFees) for _, freightFee := range freightFees { if freightFee.ErrCode == supply.ReplyOrderFreightFeeErrCodeErr { back.Err(c, freightFee.ErrMsg) return } expressFee = expressFee.Add(decimal.NewFromFloat(freightFee.FreightFee)) } 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 } else { err := fmt.Errorf("%d不支持配送", goodsInfo.ID) back.Err(c, err.Error()) return } } var brandInfo goods.Brand { if !gs.IsVipGoods() { 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).Sub(coinReducePrice) 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 preOrderInfo.OrderType = 1 preOrderInfo.IsVirtual = gs.IsVirtual 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 preOrderGoods.IsVirtual = gs.IsVirtual preOrderGoods.ExtraPrice = sku.ExtraPrice 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 }