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.

581 lines
18 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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", t.SkuID).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(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("activity_end_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("show_time_end>?", 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
}