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 ( "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
}