package ebai import ( "math" "time" "git.rosy.net.cn/baseapi/platformapi/autonavi" "git.rosy.net.cn/baseapi/platformapi/ebaiapi" "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/business/jxcallback/scheduler" "git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/business/partner" "git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/globals/api" ) const ( // acceptOrderDelay = 180 * time.Second pickupOrderDelay = 260 * time.Second callDeliveryDelay = 10 * time.Minute callDeliveryDelayGap = 30 fakeUserApplyCancel = "fake_user_apply_cancel" fakeUserUndoApplyCancel = "fake_user_undo_apply_cancel" fakeAcceptOrder = "fake_accept_order" fakeOrderAdjustFinished = "fake_order_adjust_finished" ) // 饿百的接单会直接召唤配送,为了统一将饿百的接单影射成拣货完成,然后模拟一个接单消息 var ( VendorStatus2StatusMap = map[string]int{ ebaiapi.OrderStatusNew: model.OrderStatusNew, fakeAcceptOrder: model.OrderStatusAccepted, ebaiapi.OrderStatusAccepted: model.OrderStatusFinishedPickup, ebaiapi.OrderStatusCourierAccepted: model.OrderStatusDelivering, ebaiapi.OrderStatusCourierPickedup: model.OrderStatusDelivering, ebaiapi.OrderStatusFinished: model.OrderStatusFinished, ebaiapi.OrderStatusCanceled: model.OrderStatusCanceled, fakeOrderAdjustFinished: model.OrderStatusAdjust, fakeUserApplyCancel: model.OrderStatusApplyCancel, fakeUserUndoApplyCancel: model.OrderStatusUndoApplyCancel, } ) func (p *PurchaseHandler) GetStatusFromVendorStatus(vendorStatus string) int { if status, ok := VendorStatus2StatusMap[vendorStatus]; ok { return status } return model.OrderStatusUnknown } func (p *PurchaseHandler) GetOrder(vendorOrderID string) (order *model.GoodsOrder, err error) { order, _, err = p.getOrder(vendorOrderID) return order, err } func (p *PurchaseHandler) getOrder(vendorOrderID string) (order *model.GoodsOrder, orderMap map[string]interface{}, err error) { result, err := api.EbaiAPI.OrderGet(vendorOrderID) if err == nil { order = p.Map2Order(result) } return order, result, err } func (p *PurchaseHandler) Map2Order(orderData map[string]interface{}) (order *model.GoodsOrder) { result := orderData shopMap := result["shop"].(map[string]interface{}) orderMap := result["order"].(map[string]interface{}) userMap := result["user"].(map[string]interface{}) vendorOrderID := orderMap["order_id"].(string) order = &model.GoodsOrder{ VendorOrderID: vendorOrderID, VendorOrderID2: orderMap["eleme_order_id"].(string), VendorID: model.VendorIDEBAI, VendorStoreID: shopMap["baidu_shop_id"].(string), StoreID: int(utils.Str2Int64WithDefault(utils.Interface2String(shopMap["id"]), 0)), StoreName: shopMap["name"].(string), ConsigneeName: userMap["name"].(string), ConsigneeMobile: jxutils.FormalizeMobile(userMap["phone"].(string)), ConsigneeAddress: userMap["address"].(string), CoordinateType: model.CoordinateTypeBaiDu, BuyerComment: utils.TrimBlankChar(utils.Interface2String(orderMap["remark"])), ExpectedDeliveredTime: getTimeFromInterface(orderMap["send_time"]), PickDeadline: utils.DefaultTimeValue, VendorStatus: utils.Int64ToStr(utils.MustInterface2Int64(orderMap["status"])), OrderSeq: int(utils.Str2Int64(utils.Interface2String(orderMap["order_index"]))), StatusTime: getTimeFromInterface(orderMap["create_time"]), OriginalData: string(utils.MustMarshal(result)), ActualPayPrice: utils.MustInterface2Int64(orderMap["user_fee"]), Skus: []*model.OrderSku{}, } if utils.IsTimeZero(order.PickDeadline) && !utils.IsTimeZero(order.StatusTime) { order.PickDeadline = order.StatusTime.Add(pickupOrderDelay) // 饿百要求在5分钟内拣货,不然订单会被取消 } if order.ConsigneeMobile == "" { if mobileInfo, err := api.EbaiAPI.OrderPrivateInfo(vendorOrderID); err == nil { order.ConsigneeMobile = jxutils.FormalizeMobile(mobileInfo.ShortNumber) } } if order.StoreID > math.MaxInt32 { order.StoreID = 0 } order.Status = p.GetStatusFromVendorStatus(order.VendorStatus) if utils.MustInterface2Int64(orderMap["send_immediately"]) == 1 { order.BusinessType = model.BusinessTypeImmediate } else { order.BusinessType = model.BusinessTypeDingshida if utils.IsTimeZero(order.ExpectedDeliveredTime) { order.ExpectedDeliveredTime = getTimeFromInterface(orderMap["latest_send_time"]) } } deliveryGeo := userMap["coord"].(map[string]interface{}) originalLng := utils.Interface2Float64WithDefault(deliveryGeo["longitude"], 0.0) // 饿百的订单在过一段时间后,经纬度信息会变成字符串"**" originalLat := utils.Interface2Float64WithDefault(deliveryGeo["latitude"], 0.0) lng, lat, err2 := api.AutonaviAPI.CoordinateConvert(originalLng, originalLat, autonavi.CoordSysBaidu) if err2 == nil { originalLng = lng originalLat = lat order.CoordinateType = model.CoordinateTypeMars } order.ConsigneeLng = jxutils.StandardCoordinate2Int(originalLng) order.ConsigneeLat = jxutils.StandardCoordinate2Int(originalLat) products := result["products"].([]interface{})[0].([]interface{}) // discounts := result["discount"].(map[string]interface{}) for _, product2 := range products { product := product2.(map[string]interface{}) skuName := product["product_name"].(string) _, _, _, specUnit, _, specQuality := jxutils.SplitSkuName(skuName) sku := &model.OrderSku{ VendorOrderID: order.VendorOrderID, VendorID: model.VendorIDEBAI, Count: int(utils.MustInterface2Int64(product["product_amount"])), SkuID: int(utils.Str2Int64WithDefault(utils.Interface2String(product[ebaiapi.KeyCustomSkuID]), 0)), VendorSkuID: utils.Interface2String(product["baidu_product_id"]), SkuName: skuName, Weight: jxutils.FormatSkuWeight(specQuality, specUnit), // 订单信息里没有重量,只有名字里尝试找 SalePrice: utils.MustInterface2Int64(product["product_price"]), // PromotionType: int(utils.MustInterface2Int64(product["promotionType"])), } if sku.Weight == 0 { sku.Weight = 222 // 如果名字里找不到缺省给半斤左右的一个特别值 } // if product["isGift"].(bool) { // sku.SkuType = 1 // } order.Skus = append(order.Skus, sku) order.SkuCount++ order.GoodsCount += sku.Count order.SalePrice += sku.SalePrice * int64(sku.Count) order.Weight += sku.Weight * sku.Count } // setOrederDetailFee(result, order) return order } func (p *PurchaseHandler) AcceptOrRefuseOrder(order *model.GoodsOrder, isAcceptIt bool, userName string) (err error) { globals.SugarLogger.Debugf("ebai AcceptOrRefuseOrder orderID:%s, isAcceptIt:%t", order.VendorOrderID, isAcceptIt) if isAcceptIt { p.postFakeMsg(order.VendorOrderID, fakeAcceptOrder) } else { if globals.EnableEbaiStoreWrite { err = api.EbaiAPI.OrderCancel(order.VendorOrderID, ebaiapi.CancelTypeCustom, "bu") } } return err } func (p *PurchaseHandler) PickupGoods(order *model.GoodsOrder, isSelfDelivery bool, userName string) (err error) { globals.SugarLogger.Debugf("ebai PickupGoods orderID:%s, isSelfDelivery:%t", order.VendorOrderID, isSelfDelivery) if globals.EnableEbaiStoreWrite { err = api.EbaiAPI.OrderConfirm(order.VendorOrderID) } else { p.postFakeMsg(order.VendorOrderID, ebaiapi.OrderStatusAccepted) } return err } func (p *PurchaseHandler) AcceptOrRefuseFailedGetOrder(ctx *jxcontext.Context, order *model.GoodsOrder, isAcceptIt bool) (err error) { return err } func (p *PurchaseHandler) CallCourier(ctx *jxcontext.Context, order *model.GoodsOrder) (err error) { // 拣货失败后再次招唤平台配送 return err } func (p *PurchaseHandler) ConfirmReceiveGoods(ctx *jxcontext.Context, order *model.GoodsOrder) (err error) { // 投递失败后确认收到退货 return err } // 将订单从购物平台配送转为自送 func (p *PurchaseHandler) Swtich2SelfDeliver(order *model.GoodsOrder, userName string) (err error) { globals.SugarLogger.Debugf("ebai Swtich2SelfDeliver orderID:%s", order.VendorOrderID) if globals.EnableEbaiStoreWrite { err = api.EbaiAPI.OrderSwitchselfdelivery(order.VendorOrderID) } if err == nil { // 饿百不会发送配送中,模拟发送 p.postFakeMsg(order.VendorOrderID, ebaiapi.OrderStatusCourierAccepted) } return err } // 将订单从购物平台配送转为自送后又送达 func (p *PurchaseHandler) Swtich2SelfDelivered(order *model.GoodsOrder, userName string) (err error) { globals.SugarLogger.Debugf("ebai Swtich2SelfDelivered orderID:%s", order.VendorOrderID) // todo 饿百转商家自送后,没有确认送达的概念,空操作 return err } // 完全自送的门店表示开始配送 func (p *PurchaseHandler) SelfDeliverDelivering(order *model.GoodsOrder, userName string) (err error) { globals.SugarLogger.Debugf("ebai SelfDeliverDelivering orderID:%s", order.VendorOrderID) if globals.EnableEbaiStoreWrite { err = api.EbaiAPI.OrderSendOut(order.VendorOrderID, userName) } if err == nil { // 饿百不会发送配送中,模拟发送 p.postFakeMsg(order.VendorOrderID, ebaiapi.OrderStatusCourierAccepted) } return err } // 完全自送的门店表示配送完成 func (p *PurchaseHandler) SelfDeliverDelivered(order *model.GoodsOrder, userName string) (err error) { globals.SugarLogger.Debugf("ebai SelfDeliverDelivered orderID:%s", order.VendorOrderID) if globals.EnableEbaiStoreWrite { err = api.EbaiAPI.OrderComplete(order.VendorOrderID, userName) } return err } // func (c *PurchaseHandler) onOrderMsg(msg *ebaiapi.CallbackMsg) (retVal *ebaiapi.CallbackResponse) { if ebaiapi.CmdOrderCreate == msg.Cmd { retVal = c.onOrderNew(msg) } else { status := c.callbackMsg2Status(msg) var err error if status != nil { err = partner.CurOrderManager.OnOrderStatusChanged(status) } retVal = api.EbaiAPI.Err2CallbackResponse(msg.Cmd, err, nil) } return retVal } func (c *PurchaseHandler) onOrderNew(msg *ebaiapi.CallbackMsg) (response *ebaiapi.CallbackResponse) { vendorOrderID := GetOrderIDFromMsg(msg) order, orderMap, err := c.getOrder(vendorOrderID) if err == nil { if err = partner.CurOrderManager.OnOrderNew(order, order.VendorStatus); err == nil { utils.CallFuncAsync(func() { c.OnOrderDetail(orderMap, partner.CreatedPeration) }) } } return api.EbaiAPI.Err2CallbackResponse(msg.Cmd, err, map[string]interface{}{ "source_order_id": vendorOrderID, }) } func (c *PurchaseHandler) callbackMsg2Status(msg *ebaiapi.CallbackMsg) (orderStatus *model.OrderStatus) { orderID := GetOrderIDFromMsg(msg) orderStatus = &model.OrderStatus{ VendorOrderID: orderID, VendorID: model.VendorIDEBAI, OrderType: model.OrderTypeOrder, RefVendorOrderID: orderID, RefVendorID: model.VendorIDEBAI, StatusTime: utils.Timestamp2Time(msg.Timestamp), VendorStatus: msg.Cmd, } if msg.Cmd == ebaiapi.CmdOrderUserCancel { msgType := utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["type"])) cancelType := utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["cancel_type"])) orderStatus.Remark = utils.Interface2String(msg.Body["cancel_reason"]) orderStatus.VendorStatus = msg.Cmd + "-" + msgType if additionReason := utils.Interface2String(msg.Body["addition_reason"]); additionReason != "" { orderStatus.Remark += ",额外原因:" + additionReason } if cancelType == ebaiapi.OrderUserCancelTypeBeforeSale { if msgType == ebaiapi.OrderUserCancelApply { orderStatus.VendorStatus = fakeUserApplyCancel } else if msgType == ebaiapi.OrderUserCancelInvalid { orderStatus.VendorStatus = fakeUserUndoApplyCancel } } } else if msg.Cmd == ebaiapi.CmdOrderPartRefund { msgType := utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["type"])) status := utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["status"])) orderStatus.Remark = utils.Interface2String(msg.Body["reason"]) if additionReason := utils.Interface2String(msg.Body["addition_reason"]); additionReason != "" { orderStatus.Remark += ",额外原因:" + additionReason } if msgType == ebaiapi.OrderPartRefuncTypeMerchant && status == ebaiapi.OrderPartRefundSuccess { orderStatus.VendorStatus = fakeOrderAdjustFinished } } else if status, ok := msg.Body["status"]; ok { if vendorStatus, ok := status.(string); ok { orderStatus.VendorStatus = vendorStatus } else { orderStatus.VendorStatus = utils.Int64ToStr(utils.MustInterface2Int64(status)) } orderStatus.Remark = utils.Interface2String(msg.Body["reason"]) } orderStatus.Status = c.GetStatusFromVendorStatus(orderStatus.VendorStatus) return orderStatus } func (c *PurchaseHandler) GetStatusActionTimeout(order *model.GoodsOrder, statusType, status int) (params *partner.StatusActionParams) { if statusType == scheduler.TimerStatusTypeOrder && status == model.OrderStatusAccepted { params = &partner.StatusActionParams{ // PickDeadline没有设置时才有效,饿百要求在5分钟内拣货,不然订单会被取消 Timeout: pickupOrderDelay, } } else if statusType == scheduler.TimerStatusTypeOrder && status == model.OrderStatusFinishedPickup { params = &partner.StatusActionParams{ // 立即达订单有效,自配送延时召唤配送 Timeout: callDeliveryDelay, TimeoutGap: callDeliveryDelayGap, } } return params } func (c *PurchaseHandler) getOrderStoreDeliveryType(order *model.GoodsOrder) (deliveryType int) { sql := ` SELECT * FROM store_map t1 WHERE t1.vendor_store_id = ? AND t1.vendor_id = ? AND t1.deleted_at = ? ` db := dao.GetDB() var storeMap *model.StoreMap if err := dao.GetRow(db, &storeMap, sql, order.VendorStoreID, model.VendorIDEBAI, utils.DefaultTimeValue); err == nil { return int(storeMap.DeliveryType) } else if !dao.IsNoRowsError(err) { globals.SugarLogger.Warnf("getOrderStoreDeliveryType orderID:%s failed with error:%v", order.VendorOrderID, err) } return scheduler.StoreDeliveryTypeByPlatform } func (c *PurchaseHandler) postFakeMsg(vendorOrderID, vendorStatus string) { msg := &ebaiapi.CallbackMsg{ Cmd: ebaiapi.CmdOrderStatus, Timestamp: time.Now().Unix(), Body: map[string]interface{}{ "status": vendorStatus, "order_id": vendorOrderID, }, } utils.CallFuncAsync(func() { OnCallbackMsg(msg) }) } func getTimeFromInterface(timeValue interface{}) time.Time { var timeStamp int64 if timeStr, ok := timeValue.(string); ok { timeStamp = utils.Str2Int64WithDefault(timeStr, 0) } else { timeStamp = utils.Interface2Int64WithDefault(timeValue, 0) } if timeStamp < 1538103149 { // 立即达订单给的是1(而不是空,0),1538103149不是特殊值,只是一个任意之前的时间,这样写可以处理 return utils.DefaultTimeValue } return utils.Timestamp2Time(timeStamp) } func (c *PurchaseHandler) GetOrderRealMobile(ctx *jxcontext.Context, order *model.GoodsOrder) (mobile string, err error) { mobile, err = api.EbaiAPI.GetRealMobile4Order(order.VendorOrderID) return mobile, err } func (c *PurchaseHandler) AgreeOrRefuseCancel(ctx *jxcontext.Context, order *model.GoodsOrder, isAgree bool, reason string) (err error) { if globals.EnableEbaiStoreWrite { if isAgree { err = api.EbaiAPI.OrderAgreeRefund(order.VendorOrderID) } else { err = api.EbaiAPI.OrderDisagreeRefund(order.VendorOrderID, reason) } } return err } func (c *PurchaseHandler) CancelOrder(ctx *jxcontext.Context, order *model.GoodsOrder, reason string) (err error) { if globals.EnableEbaiStoreWrite { err = api.EbaiAPI.OrderCancel(order.VendorOrderID, ebaiapi.CancelTypeCustom, reason) } return err } func (c *PurchaseHandler) AdjustOrder(ctx *jxcontext.Context, order *model.GoodsOrder, removedSkuList []*model.OrderSku, reason string) (err error) { var skuList []*ebaiapi.RefundSku for _, sku := range removedSkuList { skuList = append(skuList, &ebaiapi.RefundSku{ CustomeSkuID: utils.Int2Str(jxutils.GetSkuIDFromOrderSku(sku)), Number: utils.Int2Str(sku.Count), }) } if globals.EnableEbaiStoreWrite { err = api.EbaiAPI.OrderPartRefund(order.VendorOrderID, skuList) } return err }