package ebai import ( "encoding/json" "math" "strings" "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/jxutils/tasksch" "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 = 270 * time.Second fakePickedUp = "9527" ) var ( VendorStatus2StatusMap = map[string]int{ ebaiapi.OrderStatusNew: model.OrderStatusNew, ebaiapi.OrderStatusAccepted: model.OrderStatusAccepted, fakePickedUp: model.OrderStatusFinishedPickup, ebaiapi.OrderStatusCourierAccepted: model.OrderStatusDelivering, ebaiapi.OrderStatusCourierPickedup: model.OrderStatusDelivering, ebaiapi.OrderStatusFinished: model.OrderStatusFinished, ebaiapi.OrderStatusCanceled: model.OrderStatusCanceled, } ) 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) { result, err := api.EbaiAPI.OrderGet(vendorOrderID) // globals.SugarLogger.Info(result) if err == nil { order = p.Map2Order(result) } return order, 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: userMap["phone"].(string), ConsigneeAddress: userMap["address"].(string), CoordinateType: model.CoordinateTypeBaiDu, BuyerComment: utils.TrimBlankChar(utils.Interface2String(orderMap["remark"])), ExpectedDeliveredTime: getTimeFromTimestampStr(utils.Interface2String(orderMap["send_time"])), PickDeadline: utils.DefaultTimeValue, VendorStatus: utils.Int64ToStr(utils.MustInterface2Int64(orderMap["status"])), OrderSeq: int(utils.Str2Int64(utils.Interface2String(orderMap["order_index"]))), StatusTime: getTimeFromTimestampStr(utils.Interface2String(orderMap["create_time"])), OriginalData: string(utils.MustMarshal(result)), ActualPayPrice: utils.MustInterface2Int64(orderMap["user_fee"]), Skus: []*model.OrderSku{}, } if jxutils.IsMobileFake(order.ConsigneeMobile) { if mobileInfo, err := api.EbaiAPI.OrderPrivateInfo(vendorOrderID); err == nil { order.ConsigneeMobile = 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 } deliveryGeo := userMap["coord"].(map[string]interface{}) originalLng := utils.Interface2FloatWithDefault(deliveryGeo["longitude"], 0.0) // 饿百的订单在过一段时间后,经纬度信息会变成字符串"**" originalLat := utils.Interface2FloatWithDefault(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", order.VendorOrderID) if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite { if isAcceptIt { err = api.EbaiAPI.OrderConfirm(order.VendorOrderID) } else { err = api.EbaiAPI.OrderCancel(order.VendorOrderID, ebaiapi.CancelTypeCustom, "bu") } } return err } func (p *PurchaseHandler) PickupGoods(order *model.GoodsOrder, userName string) (err error) { globals.SugarLogger.Debugf("ebai PickupGoods orderID:%s", order.VendorOrderID) if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite { err = api.EbaiAPI.OrderCallDelivery(order.VendorOrderID) } p.postFakeFinishedPickupMsg(order.VendorOrderID) // 饿百没有拣货完成事件,模拟发送 return err } // 将订单从购物平台配送转为自送 func (p *PurchaseHandler) Swtich2SelfDeliver(order *model.GoodsOrder, userName string) (err error) { globals.SugarLogger.Debugf("ebai Swtich2SelfDeliver orderID:%s", order.VendorOrderID) if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite { err = api.EbaiAPI.OrderSwitchselfdelivery(order.VendorOrderID) } 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.EnableStoreWrite && globals.EnableEbaiStoreWrite { err = api.EbaiAPI.OrderSendOut(order.VendorOrderID, userName) } return err } // 完全自送的门店表示配送完成 func (p *PurchaseHandler) SelfDeliverDelievered(order *model.GoodsOrder, userName string) (err error) { globals.SugarLogger.Debugf("ebai SelfDeliverDelievered orderID:%s", order.VendorOrderID) if globals.EnableStoreWrite && 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) if status != nil { err := partner.CurOrderManager.OnOrderStatusChanged(status) // 如果订单所属的门店是专送模式,直接跳到拣货完成,因为饿百没有拣货完成的概念,接单就视为拣货完成 if status.Status == model.OrderStatusAccepted { postFakeFinishedPickupMsg := true vendorOrderID := GetOrderIDFromMsg(msg) if order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, model.VendorIDEBAI); err == nil { if c.getOrderStoreDeliveryType(order) != scheduler.StoreDeliveryTypeByPlatform { postFakeFinishedPickupMsg = false } } if postFakeFinishedPickupMsg { c.postFakeFinishedPickupMsg(vendorOrderID) } } retVal = api.EbaiAPI.Err2CallbackResponse(msg.Cmd, err, nil) } else { retVal = api.EbaiAPI.Err2CallbackResponse(msg.Cmd, nil, nil) } } return retVal } func (c *PurchaseHandler) onOrderNew(msg *ebaiapi.CallbackMsg) (response *ebaiapi.CallbackResponse) { vendorOrderID := GetOrderIDFromMsg(msg) order, err := c.GetOrder(vendorOrderID) if err == nil { err = partner.CurOrderManager.OnOrderNew(order, order.VendorStatus) } return api.EbaiAPI.Err2CallbackResponse(msg.Cmd, err, map[string]interface{}{ "source_order_id": vendorOrderID, }) } func (c *PurchaseHandler) callbackMsg2Status(msg *ebaiapi.CallbackMsg) (orderStatus *model.OrderStatus) { if status, ok := msg.Body["status"]; ok { vendorStatus := utils.Int64ToStr(utils.MustInterface2Int64(status)) orderID := GetOrderIDFromMsg(msg) orderStatus = &model.OrderStatus{ VendorOrderID: orderID, VendorID: model.VendorIDEBAI, OrderType: model.OrderTypeOrder, RefVendorOrderID: orderID, RefVendorID: model.VendorIDEBAI, VendorStatus: vendorStatus, Status: c.GetStatusFromVendorStatus(vendorStatus), StatusTime: utils.Timestamp2Time(msg.Timestamp), Remark: utils.Interface2String(msg.Body["reason"]), } } else { globals.SugarLogger.Infof("ebai callbackMsg2Status can not find status field in msg:%s", utils.Format4Output(msg, false)) } return orderStatus } func (c *PurchaseHandler) GetStatusActionTimeout(statusType, status int) time.Duration { if statusType == scheduler.TimerStatusTypeOrder && status == model.OrderStatusNew { return acceptOrderDelay // 饿百开了专送店的订单没有拣货状态,接单后就为拣货完成,所以要延迟接单,否则门店来不及备货 } return 0 } 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) postFakeFinishedPickupMsg(vendorOrderID string) { msg := &ebaiapi.CallbackMsg{ Cmd: ebaiapi.CmdOrderStatus, Timestamp: time.Now().Unix(), Body: map[string]interface{}{ "status": json.Number(fakePickedUp), // json.Number实际是string "order_id": vendorOrderID, }, } go func() { OnCallbackMsg(msg) }() } func getTimeFromTimestampStr(sendTime string) time.Time { timeStamp := utils.Str2Int64WithDefault(sendTime, 0) if timeStamp < 1538103149 { // 立即达订单给的是1(而不是空,0),1538103149不是特殊值,只是一个任意之前的时间,这样写可以处理 return utils.DefaultTimeValue } return utils.Timestamp2Time(timeStamp) } func (c *PurchaseHandler) RefreshRealMobile(ctx *jxcontext.Context, fromTime, toTime time.Time, isAsync, isContinueWhenError bool) (hint string, err error) { sql := ` SELECT * FROM goods_order WHERE vendor_id = ? AND consignee_mobile2 = '' AND order_created_at <= ? ` sqlParams := []interface{}{ model.VendorIDEBAI, time.Now().Add(-4 * time.Hour), } if !utils.IsTimeZero(fromTime) { sql += " AND order_created_at >= ?" sqlParams = append(sqlParams, fromTime) } if !utils.IsTimeZero(toTime) { sql += " AND order_created_at <= ?" sqlParams = append(sqlParams, toTime) } var orderList []*model.GoodsOrder db := dao.GetDB() if err = dao.GetRows(db, &orderList, sql, sqlParams...); err == nil && len(orderList) > 0 { task := tasksch.NewParallelTask("ebai RefreshRealMobile", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx.GetUserName(), func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { order := batchItemList[0].(*model.GoodsOrder) mobile, err2 := api.EbaiAPI.GetRealMobile4Order(order.VendorOrderID) if err = err2; err == nil && !jxutils.IsMobileFake(mobile) && strings.Index(order.ConsigneeMobile, mobile) == -1 { order.ConsigneeMobile2 = mobile _, err = dao.UpdateEntity(db, order, "ConsigneeMobile2") } return nil, err }, orderList) ctx.SetTaskOrAddChild(task, nil) tasksch.ManageTask(task).Run() hint = task.ID if !isAsync { _, err = task.GetResult(0) } } return hint, err }