package order
import (
"errors"
"fmt"
"git.oa00.com/go/mysql"
"log"
"recook/configs"
"recook/internal/back"
"recook/internal/dbc"
"recook/internal/libs/snow"
"recook/internal/model/goods"
"recook/internal/model/order"
"recook/internal/model/order_preview"
"recook/internal/model/shopping_trolley"
"recook/internal/model/user"
"recook/internal/v2/lib/jcook"
"recook/internal/v2/lib/shama"
"recook/internal/v2/lib/supply"
"recook/internal/v2/model/jyy"
goods2 "recook/internal/v2/model/recook/goods"
"recook/tools"
"strconv"
"github.com/gin-gonic/gin"
"github.com/golangkit/formatime"
"github.com/jinzhu/gorm"
"github.com/shopspring/decimal"
)
/*创建普通订单的参数*/
type submitOrderParam struct {
UserID uint ` json:"userId" `
PreviewOrderID uint ` json:"previewOrderId" `
}
//var submitOrderSentinel sync.Mutex
func SubmitOrder ( c * gin . Context ) {
var p submitOrderParam
err := tools . ParseParams ( & p , c )
if err != nil {
back . Fail ( c , err . Error ( ) )
return
}
var previewOrderInfo order_preview . Information
if err = dbc . DB . First ( & previewOrderInfo , "id = ?" , p . PreviewOrderID ) . Error ; err != nil {
back . Err ( c , err . Error ( ) )
return
}
orderInfo := ( & order . Information { } ) . Reflect ( & previewOrderInfo )
if orderInfo . OrderType == 2 {
orderInfo . ExpireTime = formatime . NewSecondFrom ( orderInfo . ExpireTime . Time . AddDate ( 100 , 0 , 0 ) )
}
var u user . Information
dbc . DB . First ( & u , "id = ?" , orderInfo . UserID )
if orderInfo . OrderType == 2 && ( u . Level < 2 || ( u . Level == 2 && ! u . IsOffline ) ) {
back . Err ( c , "订单不合法" )
return
}
// 检测地址
var previewOrderAddr order_preview . Addr
{
if ! previewOrderInfo . IsVirtual {
if err = dbc . DB . First ( & previewOrderAddr , "order_id = ?" , p . PreviewOrderID ) . Error ; err != nil {
back . Err ( c , err . Error ( ) )
return
}
if previewOrderAddr . AddressID == 0 {
back . Err ( c , "请选择收货地址" )
return
}
}
}
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 { }
for _ , v := range preOrderGoodsList {
var goodsInfo goods . Information
{
if err = dbc . DB . First ( & goodsInfo , "id = ?" , v . GoodsID ) . Error ; err != nil {
back . Err ( c , err . Error ( ) )
return
}
if ! configs . IsProductionEnv ( ) {
if goodsInfo . ThirdPartyType != 0 && goodsInfo . ThirdPartyType != goods2 . RecookGoodsInfoThirdPartyTypeSupply {
back . Fail ( c , "测试环境无法购买" )
return
}
}
if goodsInfo . Hash != v . Hash {
back . Fail ( c , "商品信息已经发生变化,请重新下单" )
return
}
cc := goodsInfo . PublishStatus == 0
if orderInfo . OrderType == 2 {
cc = goodsInfo . SalePublish == 0
}
if cc {
back . Fail ( c , "商品已下架:" + goodsInfo . GoodsName )
return
}
}
var sku goods . Sku
{
if ! goodsInfo . IsVipGoods ( ) {
if err = dbc . DB . Select ( "id, inventory, sale_inventory" ) . First ( & sku , "id = ?" , v . SkuID ) . Error ; err != nil {
back . Err ( c , err . Error ( ) )
return
}
// 仅考虑商品库存
condition := orderInfo . OrderType != 2 // 批发不需要验证库存
//if orderInfo.OrderType == 2 {
// condition = v.Quantity > sku.SaleInventory
//}
if condition {
if v . Quantity > sku . Inventory {
back . Fail ( c , v . GoodsName + "已被抢光啦" )
return
}
}
preGoodSkuMap [ v . ID ] = & sku
}
if goodsInfo . IsVipGoods ( ) && u . IsVip ( ) {
back . Fail ( c , "无须购买会员卡" )
return
}
}
}
var previewOrderShoppingTrolleyInfos [ ] order_preview . ShoppingTrolleyInfo
if orderInfo . OrderType == 1 && previewOrderInfo . Channel == 1 {
err = dbc . DB . Find ( & previewOrderShoppingTrolleyInfos , "order_id = ?" , p . PreviewOrderID ) . Error
if err != nil && ! gorm . IsRecordNotFoundError ( err ) {
back . Err ( c , err . Error ( ) )
return
}
}
data := make ( map [ uint ] [ ] order_preview . GoodsDetail )
fData := make ( map [ uint ] decimal . Decimal )
virtualID := GetVirtualID ( )
tx := dbc . DB . Begin ( )
{
for _ , value := range preOrderGoodsList {
data [ value . VendorID ] = append ( data [ value . VendorID ] , value )
fData [ value . VendorID ] = fData [ value . VendorID ] . Add ( value . ExpressFee )
}
work := snow . GetOrderWorker ( )
orderID , err := work . NextID ( )
if err != nil {
back . Err ( c , err . Error ( ) )
return
}
condition := len ( data ) == 1
orderInfo . ID = uint ( orderID )
for vendor , value := range data {
id1 , err := work . NextID ( )
if err != nil {
back . Err ( c , err . Error ( ) )
return
}
orderCopy := order . Information {
ID : uint ( id1 ) ,
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 ,
Cost : orderInfo . Cost ,
OrderType : orderInfo . OrderType ,
IsSplit : orderInfo . IsSplit ,
}
kind := 0
switch vendor {
case 1800 , 2000 :
channelOrderID := strconv . Itoa ( int ( orderCopy . ID ) )
resp , err := JdOrderSubmit ( tx , c . ClientIP ( ) , data , vendor , previewOrderAddr , fData , channelOrderID )
if err != nil {
log . Println ( "订单生成失败:" , err . Error ( ) )
back . Err ( c , err . Error ( ) )
tx . Rollback ( )
return
}
if vendor == 1800 {
kind = 1
} else {
kind = 2
}
orderCopy . Kind = kind
fmt . Println ( "订单号为" , resp . OrderID )
orderCopy . JCookOrderID = resp . OrderID
case 3000 :
channelOrderID := strconv . Itoa ( int ( orderCopy . ID ) )
resp , err := ShaMaOrderSubmit ( tx , c . ClientIP ( ) , data , vendor , previewOrderAddr , fData , channelOrderID )
if err != nil {
log . Println ( "订单生成失败:" , err . Error ( ) )
back . Err ( c , err . Error ( ) )
tx . Rollback ( )
return
}
orderCopy . Kind = 3
orderCopy . ShaMaOrderID = resp . OrderID
case 4000 :
channelOrderID := strconv . Itoa ( int ( orderCopy . ID ) )
thirdPartyOrderSn , err := SupplyOrderSubmit ( tx , c . ClientIP ( ) , data , vendor , previewOrderAddr , fData , channelOrderID )
if err != nil {
log . Println ( "订单生成失败:" , err . Error ( ) )
back . Err ( c , err . Error ( ) )
tx . Rollback ( )
return
}
orderCopy . Kind = 4
orderCopy . ThirdPartyType = goods2 . RecookGoodsInfoThirdPartyTypeSupply
orderCopy . ThirdPartyOrderSn = thirdPartyOrderSn
}
if condition {
orderInfo . ID = orderCopy . ID
orderCopy . VirtualID = orderCopy . ID
}
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
}
for _ , v := range value {
goodsDetail := ( & order . GoodsDetail { } ) . Reflect ( & v , & orderCopy )
if err = tx . Create ( goodsDetail ) . Error ; err != nil {
back . Err ( c , err . Error ( ) )
tx . Rollback ( )
return
}
sku := preGoodSkuMap [ v . ID ]
if orderCopy . OrderType == 1 && ! orderCopy . IsSplit && sku . ID != 0 {
// 仅考虑商品库存的情况
if err = tx . Model ( sku ) . UpdateColumn ( "inventory" , gorm . Expr ( "inventory - ?" , v . Quantity ) ) . Error ; err != nil {
back . Err ( c , err . Error ( ) )
tx . Rollback ( )
return
}
}
if orderInfo . OrderType == 1 && ! orderCopy . IsSplit {
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 orderInfo . OrderType == 2 {
if err = tx . Model ( sku ) . UpdateColumn ( "sale_volume" , gorm . Expr ( "sale_volume + ?" , v . Quantity ) ) . 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
}
}
if orderInfo . OrderType == 2 && orderInfo . Channel == 1 {
ids := make ( [ ] uint , 0 )
for _ , v := range preGoodSkuMap {
ids = append ( ids , v . ID )
tx . Delete ( & jyy . ShopCartEntry { } , "sku_id in (?) and user_id = ?" , ids , p . UserID )
}
}
}
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 {
if err . Error ( ) == "商品价格不匹配" {
go func ( ) {
rq := jcook . SkuPriceReq { }
for _ , info := range t {
rq . SkuIDSet = append ( rq . SkuIDSet , info . SkuID )
}
var res [ ] jcook . SkuPriceResp
if err := client . Exec ( rq , & res ) ; err != nil {
return
}
if len ( res ) == 0 {
return
}
for _ , t := range res {
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
}
}
} ( )
return jcook . OrderSubmitResp { } , errors . New ( "价格变动,请重新下单" )
}
return jcook . OrderSubmitResp { } , err
}
return resp , nil
}
func ShaMaOrderSubmit ( tx * gorm . DB , ip string , data map [ uint ] [ ] order_preview . GoodsDetail , vendor uint , previewOrderAddr order_preview . Addr , fData map [ uint ] decimal . Decimal , channelOrderID string ) ( shama . OrderSubmitResp , error ) {
client := shama . GetClient ( )
t := make ( [ ] shama . 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 shama . OrderSubmitResp { } , err
}
cid , _ := strconv . Atoi ( sku . ThirdPartySkuId )
t = append ( t , shama . 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 := shama . OrderSubmitReq {
Address : addr ,
SkuList : t ,
OrderFee : orderFee ,
Receiver : shama . OrderReceiver {
Name : previewOrderAddr . ReceiverName ,
Mobile : previewOrderAddr . Mobile ,
} ,
FreightFee : fData [ vendor ] ,
UserIp : ip ,
ChannelOrderID : channelOrderID ,
}
var resp shama . OrderSubmitResp
if err := client . Exec ( req1 , & resp ) ; err != nil {
return shama . OrderSubmitResp { } , err
}
return resp , nil
}
func SupplyOrderSubmit ( tx * gorm . DB , ip string , data map [ uint ] [ ] order_preview . GoodsDetail , vendor uint , previewOrderAddr order_preview . Addr , fData map [ uint ] decimal . Decimal , channelOrderID string ) ( supplyOrderSn string , err error ) {
orderFee := decimal . Zero
var freightFeeItems [ ] supply . OrderFreightFeeItem
for _ , j := range data [ vendor ] {
var sku goods . Sku
if err := tx . First ( & sku , "id = ?" , j . SkuID ) . Error ; err != nil {
return supplyOrderSn , errors . New ( "商品错误" )
}
supplySkuId , _ := strconv . Atoi ( sku . ThirdPartySkuId )
freightFeeItems = append ( freightFeeItems , supply . OrderFreightFeeItem {
SkuId : uint ( supplySkuId ) ,
Quantity : j . Quantity ,
Price : sku . PurchasePrice ,
} )
orderFee = orderFee . Add ( sku . PurchasePrice . Mul ( decimal . NewFromInt ( int64 ( j . Quantity ) ) ) )
}
addr := previewOrderAddr . Province + previewOrderAddr . City + previewOrderAddr . District + previewOrderAddr . Address
freightFees , err := supply . Api . Order . FreightFee ( addr , freightFeeItems )
if err != nil {
return
}
var orderFreightFees [ ] supply . OrderFreightFee
totalFreightFee := decimal . Zero
for _ , freightFee := range freightFees {
orderFreightFees = append ( orderFreightFees , supply . OrderFreightFee {
SkuIds : freightFee . SkuIds ,
FreightFee : decimal . NewFromFloat ( freightFee . FreightFee ) ,
} )
totalFreightFee = totalFreightFee . Add ( decimal . NewFromFloat ( freightFee . FreightFee ) )
}
if fData [ vendor ] . Cmp ( totalFreightFee ) != 0 {
return supplyOrderSn , errors . New ( "运费错误" )
}
submit , err := supply . Api . Order . Submit ( supply . OrderSubmit {
ChannelOrderSn : channelOrderID ,
Address : addr ,
Skus : freightFeeItems ,
Receiver : supply . Receiver {
Name : previewOrderAddr . ReceiverName ,
Mobile : previewOrderAddr . Mobile ,
Email : "shangmenghaishi@163.com" ,
ZipCode : "315000" ,
} ,
OrderFee : orderFee ,
FreightFees : orderFreightFees ,
UserIp : ip ,
} )
if err != nil {
return supplyOrderSn , err
}
return submit . OrderSn , nil
}
func GetVirtualID ( ) uint {
id , _ := snow . GetOrderWorker ( ) . NextID ( )
return uint ( id )
}