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.

481 lines
14 KiB

4 years ago
package order
import (
"fmt"
"github.com/shopspring/decimal"
"log"
order_preview2 "recook/internal/api/mobile/order_preview"
"recook/internal/back"
"recook/internal/dbc"
"recook/internal/model/coupon"
"recook/internal/model/goods"
"recook/internal/model/order"
"recook/internal/model/order_preview"
"recook/internal/model/promotion"
"recook/internal/model/shopping_trolley"
"recook/internal/model/user"
"recook/internal/v2/controller/task"
"recook/internal/v2/lib/jcook"
goods3 "recook/internal/v2/model/recook/goods"
"recook/tools"
"strconv"
"sync"
"time"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
)
/*创建普通订单的参数*/
type submitOrderParam struct {
UserID uint `json:"userId"`
PreviewOrderID uint `json:"previewOrderId"`
}
var submitOrderSentinel sync.Mutex
/*
10
/
使
*/
func SubmitOrder(c *gin.Context) {
var p submitOrderParam
err := tools.ParseParams(&p, c)
if err != nil {
back.Fail(c, err.Error())
return
}
submitOrderSentinel.Lock()
defer submitOrderSentinel.Unlock()
// 验证库存 验证优惠券是否失效 验证活动 验证金额是否匹配
now := time.Now().Unix()
var previewOrderInfo order_preview.Information
if err = dbc.DB.First(&previewOrderInfo, "id = ?", p.PreviewOrderID).Error; err != nil {
back.Err(c, err.Error())
return
}
// 检测地址
var previewOrderAddr order_preview.Addr
{
if err = dbc.DB.First(&previewOrderAddr, "order_id = ?", p.PreviewOrderID).Error; err != nil {
back.Err(c, err.Error())
return
}
if previewOrderInfo.ShippingMethod == 0 && previewOrderAddr.AddressID == 0 {
back.Err(c, "请选择收货地址")
return
}
if previewOrderAddr.IsDeliveryArea == 0 && previewOrderInfo.ShippingMethod == 0 {
back.Err(c, "该地区有商品不发货,请先更换地址")
return
}
}
// 钱包的瑞币是否足够支付
var wallet user.Wallet
{
if err = dbc.DB.First(&wallet, "user_id = ?", p.UserID).Error; err != nil {
back.Err(c, err.Error())
return
}
if wallet.Coin.LessThan(previewOrderInfo.CoinTotalAmount) {
back.Fail(c, "瑞币不足,请重新购买")
return
}
}
// 优惠券是否可用
var preOrderCouponList []order_preview.CouponDetail
if err = dbc.DB.Find(&preOrderCouponList, "order_id = ?", p.PreviewOrderID).Error; err != nil && false == gorm.IsRecordNotFoundError(err) {
back.Err(c, err.Error())
return
}
personalCouponMidList := make([]*coupon.ReceiverMid, 0, 0)
for _, v := range preOrderCouponList {
var couponMid coupon.ReceiverMid
if err = dbc.DB.First(&couponMid, "id = ?", v.PersonalCouponID).Error; err != nil {
back.Err(c, err.Error())
return
}
if couponMid.Status == 1 {
back.Fail(c, "优惠券已在其他订单使用,请重新下单")
return
}
if couponMid.EndTime.Time.Unix() < now {
back.Fail(c, "优惠券已过期,请重新下单")
return
}
personalCouponMidList = append(personalCouponMidList, &couponMid)
}
var preOrderGoodsList []order_preview.GoodsDetail
if err = dbc.DB.Find(&preOrderGoodsList, "order_id = ?", p.PreviewOrderID).Error; err != nil {
back.Err(c, err.Error())
return
}
// 商品信息是否变化了
// 活动是否失效了
// 有没有库存
preGoodSkuMap := map[uint]*goods.Sku{}
prePromotionSkuMap := map[uint]*promotion.Sku{}
// 是否需要实名认证
hasAuth := false
recookGoodsInfoModel := &goods3.RecookGoodsInfoModel{}
for _, v := range preOrderGoodsList {
if !previewOrderInfo.CoinTotalAmount.IsZero() && !recookGoodsInfoModel.HasCoin(v.Storehouse) {
back.Err(c, "订单含保税仓或海外仓商品,无法使用瑞币抵扣")
return
}
var goodsInfo goods.Information
{
if err = dbc.DB.Select("hash, publish_status,third_party_id,third_party_type").First(&goodsInfo, "id = ?", v.GoodsID).Error; err != nil {
back.Err(c, err.Error())
return
}
if goodsInfo.ThirdPartyType == goods3.RecookGoodsInfoThirdPartyTypeJingtong {
jingtong := &task.Jingtong{}
if err := jingtong.Update(goodsInfo.ThirdPartyId); err != nil {
back.Err(c, err.Error())
return
}
if err = dbc.DB.Select("hash, publish_status,third_party_id,third_party_type").First(&goodsInfo, "id = ?", v.GoodsID).Error; err != nil {
back.Err(c, err.Error())
return
}
}
if goodsInfo.Hash != v.Hash {
back.Fail(c, "商品信息已经发生变化,请重新下单")
return
}
if goodsInfo.PublishStatus == 0 {
back.Fail(c, "商品已下架:"+goodsInfo.GoodsName)
return
}
if goodsInfo.HasAuth == 1 {
hasAuth = true
}
}
var sku goods.Sku
var promotionSku promotion.Sku
{
if err = dbc.DB.Select("id, inventory").First(&sku, "id = ?", v.SkuID).Error; err != nil {
back.Err(c, err.Error())
return
}
// 仅考虑商品库存
dbc.DB.First(&promotionSku, v.PromotionSkuId)
prePromotionSkuMap[v.ID] = &promotionSku
if v.Quantity > sku.Inventory {
back.Fail(c, v.GoodsName+"已被抢光啦")
return
}
preGoodSkuMap[v.ID] = &sku
}
}
var previewOrderShoppingTrolleyInfos []order_preview.ShoppingTrolleyInfo
if previewOrderInfo.Channel == 1 {
err = dbc.DB.Find(&previewOrderShoppingTrolleyInfos, "order_id = ?", p.PreviewOrderID).Error
if err != nil && false == gorm.IsRecordNotFoundError(err) {
back.Err(c, err.Error())
return
}
}
orderInfo := (&order.Information{}).Reflect(&previewOrderInfo)
// 加入新的 权限信息
var userInfo user.Information
dbc.DB.Find(&userInfo, p.UserID)
// 判断实名认证
if hasAuth && userInfo.RealInfoStatus != user.RealInfoPass {
back.Err(c, "请先进行实名认证")
return
}
data := make(map[uint][]order_preview.GoodsDetail)
fData := make(map[uint]decimal.Decimal)
var lastId uint
virtualID := GetVirtualID()
tx := dbc.DB.Begin()
{
sql := `select max(id)+floor(1+rand()*50) from recook_order_info`
if err = tx.Raw(sql).Count(&lastId).Error; err != nil {
back.Err(c, err.Error())
tx.Rollback()
return
}
for _, value := range preOrderGoodsList {
data[value.VendorID] = append(data[value.VendorID], value)
fData[value.VendorID] = fData[value.VendorID].Add(value.ExpressFee)
}
condition := len(data) == 1
orderInfo.ID = lastId
var count uint = 0
for vendor, value := range data {
//if vendor == 1800 || vendor== 2000 {
// continue
//}
orderCopy := order.Information{
ID: lastId + count,
AncestorID: orderInfo.AncestorID,
ParentID: orderInfo.ParentID,
SharerID: orderInfo.SharerID,
LiveId: orderInfo.LiveId,
UserID: orderInfo.UserID,
Title: value[0].GoodsName,
BrandCouponTotalAmount: decimal.Decimal{},
UniverseCouponTotalAmount: decimal.Decimal{},
CoinTotalAmount: decimal.Decimal{},
ExpressTotalFee: decimal.Decimal{},
GoodsTotalAmount: decimal.Decimal{},
GoodsTotalCommission: decimal.Decimal{},
ActualTotalAmount: decimal.Decimal{},
Channel: orderInfo.Channel,
ShippingMethod: orderInfo.ShippingMethod,
StoreID: orderInfo.StoreID,
BuyerMessage: orderInfo.BuyerMessage,
Status: orderInfo.Status,
ExpressStatus: orderInfo.ExpressStatus,
InvoiceStatus: orderInfo.InvoiceStatus,
EvaluatedAt: orderInfo.EvaluatedAt,
CreatedAt: orderInfo.CreatedAt,
ExpireTime: orderInfo.ExpireTime,
PayIP: orderInfo.PayIP,
TradeNo: orderInfo.TradeNo,
PayTime: orderInfo.PayTime,
PayMethod: orderInfo.PayMethod,
CompletedAt: orderInfo.CompletedAt,
IsFirst: orderInfo.IsFirst,
VirtualID: virtualID,
}
kind := 0
if vendor == 1800 || vendor == 2000 {
channelOrderID := strconv.Itoa(int(lastId + count))
resp, err := JdOrderSubmit(tx, c.ClientIP(), data, vendor, previewOrderAddr, fData, channelOrderID)
if err != nil {
log.Println("订单生成失败:", err.Error())
back.Err(c, "订单生成失败")
tx.Rollback()
return
}
if vendor == 1800 {
kind = 1
} else {
kind = 2
}
orderCopy.Kind = kind
fmt.Println("订单号为", resp.OrderID)
orderCopy.JCookOrderID = resp.OrderID
}
if condition {
orderCopy.VirtualID = orderCopy.ID
}
count += 1
for _, v := range value {
orderCopy.BrandCouponTotalAmount = orderCopy.BrandCouponTotalAmount.Add(v.BrandCouponAmount)
orderCopy.UniverseCouponTotalAmount = orderCopy.UniverseCouponTotalAmount.Add(v.UniverseCouponAmount)
orderCopy.CoinTotalAmount = orderCopy.CoinTotalAmount.Add(v.CoinAmount)
orderCopy.ExpressTotalFee = orderCopy.ExpressTotalFee.Add(v.ExpressFee)
orderCopy.GoodsTotalAmount = orderCopy.GoodsTotalAmount.Add(v.GoodsAmount)
orderCopy.GoodsTotalCommission = orderCopy.GoodsTotalCommission.Add(v.TotalCommission)
orderCopy.ActualTotalAmount = orderCopy.ActualTotalAmount.Add(v.ActualAmount)
}
if err = tx.Create(&orderCopy).Error; err != nil {
back.Err(c, err.Error())
tx.Rollback()
return
}
orderAddr := (&order.Addr{}).Reflect(&previewOrderAddr, &orderCopy)
if err = tx.Create(orderAddr).Error; err != nil {
back.Err(c, err.Error())
tx.Rollback()
return
}
//判断是否为秒杀商品
smp := make(map[uint]uint)
now1 := previewOrderInfo.CreatedAt.Time
for _, v := range preOrderGoodsList {
res, _ := order_preview2.IfSecKill(now1, v.SkuID)
if res.Id > 0 {
//秒杀中
num := res.RealStock.Sub(decimal.NewFromInt(int64(v.Quantity)))
if num.LessThan(decimal.Zero) {
back.Err(c, "库存不足")
tx.Rollback()
return
} else {
//库存更新
err = tx.Table(res.TableName()).Where("id=?", res.Id).Update("real_stock", num).Error
if err != nil {
back.Err(c, err.Error())
tx.Rollback()
return
}
}
smp[v.SkuID] = res.SecKillActivityId
} else {
continue
}
}
for _, v := range value {
goodsDetail := (&order.GoodsDetail{}).Reflect(&v, &orderCopy)
if _, ok := smp[v.SkuID]; ok {
goodsDetail.ActivityStatus = 1
goodsDetail.ActivityId = smp[v.SkuID]
}
if err = tx.Create(goodsDetail).Error; err != nil {
back.Err(c, err.Error())
tx.Rollback()
return
}
// 仅考虑商品库存的情况
sku := preGoodSkuMap[v.ID]
if err = tx.Model(sku).UpdateColumn("inventory", gorm.Expr("inventory - ?", v.Quantity)).Error; err != nil {
back.Err(c, err.Error())
tx.Rollback()
return
}
if err = tx.Model(sku).UpdateColumn("sales_volume", gorm.Expr("sales_volume + ?", v.Quantity)).Error; err != nil {
back.Err(c, err.Error())
tx.Rollback()
return
}
}
if orderCopy.CoinTotalAmount.GreaterThan(decimal.NewFromFloat(0.0)) {
// 标记瑞币, 即扣除了总额
if err = tx.Model(&wallet).Updates(map[string]interface{}{
"coin": wallet.Coin.Sub(orderCopy.CoinTotalAmount),
}).Error; err != nil {
back.Err(c, err.Error())
tx.Rollback()
return
}
// 加入流水记录, 直接加在coinHistory中
var uCoinHis user.CoinHistory
uCoinHis.UserID = p.UserID
uCoinHis.CoinNum = orderCopy.CoinTotalAmount.Neg()
uCoinHis.CoinType = user.PurchasedTypeForCoinHistory
uCoinHis.OrderId = orderCopy.ID
uCoinHis.CreatedAt = orderCopy.CreatedAt
uCoinHis.Remark = "订单扣除"
if err := dbc.DB.Create(&uCoinHis).Error; err != nil {
back.Err(c, err.Error())
tx.Rollback()
return
}
walletCoinDetail := user.WalletCoinList{
UserID: wallet.UserID,
Number: int64(orderCopy.CoinTotalAmount.IntPart()),
Title: orderCopy.Title,
Comment: "订单支付抵扣",
OrderID: orderCopy.ID,
}
if err = tx.Create(&walletCoinDetail).Error; err != nil {
back.Err(c, err.Error())
tx.Rollback()
return
}
}
}
// 如果是购物车下单 则要清除一下购物车
for _, v := range previewOrderShoppingTrolleyInfos {
err = tx.Delete(shopping_trolley.Information{ID: v.TrolleyID}).Error
if err != nil {
back.Err(c, err.Error())
tx.Rollback()
return
}
}
}
tx.Commit()
if len(data) > 1 {
orderInfo.ID = virtualID
}
back.Suc(c, "提交成功,请20分钟内完成付款", orderInfo)
}
func JdOrderSubmit(tx *gorm.DB, ip string, data map[uint][]order_preview.GoodsDetail, vendor uint, previewOrderAddr order_preview.Addr, fData map[uint]decimal.Decimal, channelOrderID string) (jcook.OrderSubmitResp, error) {
client := jcook.GetClient()
t := make([]jcook.LogisticsInfo, 0)
orderFee := decimal.Zero
for _, j := range data[vendor] {
var sku goods.Sku
if err := tx.First(&sku, "id = ?", j.SkuID).Error; err != nil {
return jcook.OrderSubmitResp{}, err
}
cid, _ := strconv.Atoi(sku.ThirdPartySkuId)
t = append(t, jcook.LogisticsInfo{
SkuID: uint(cid),
Quantity: j.Quantity,
SkuPrice: sku.PurchasePrice,
})
amount := sku.PurchasePrice.Mul(decimal.NewFromInt(int64(j.Quantity)))
orderFee = orderFee.Add(amount)
}
addr := previewOrderAddr.Province + previewOrderAddr.City + previewOrderAddr.District + previewOrderAddr.Address
req1 := jcook.OrderSubmitReq{
Address: addr,
SkuList: t,
OrderFee: orderFee,
Receiver: jcook.OrderReceiver{
Name: previewOrderAddr.ReceiverName,
Mobile: previewOrderAddr.Mobile,
},
FreightFee: fData[vendor],
UserIp: ip,
ChannelOrderID: channelOrderID,
}
var resp jcook.OrderSubmitResp
if err := client.Exec(req1, &resp); err != nil {
return jcook.OrderSubmitResp{}, err
}
return resp, nil
}
const initCount int64 = 20210624681
func GetVirtualID() uint {
result := dbc.Rds.Incr("order_count")
if r, err := result.Result(); err != nil {
log.Println(err.Error())
return uint(time.Now().Unix()) * 10
} else {
return uint(r + initCount)
}
}