package ebai import ( "fmt" "math" "strings" "time" "git.rosy.net.cn/jx-callback/business/jxutils/tasksch" "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 pickupOrderDelay = 1 * time.Minute callDeliveryDelay = 10 * time.Minute callDeliveryDelayGap = 30 fakeAcceptOrder = "fake_accept_order" fakeOrderAdjustFinished = "fake_order_adjust_finished" fakeOrderCanceled = "fake_order_canceled" ) // 饿百的接单会直接召唤配送,为了统一将饿百的接单影射成拣货完成,然后模拟一个接单消息 var ( VendorStatus2StatusMap = map[string]int{ ebaiapi.CmdOrderCreate: model.OrderStatusWaitAccepted, ebaiapi.OrderStatusNew: model.OrderStatusWaitAccepted, 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, fakeOrderCanceled: model.OrderStatusCanceled, } skuActTypeMap = map[string]int{ ebaiapi.OrderSkuDiscountTypeZhe: 1, ebaiapi.OrderSkuDiscountTypeReduce: 1, ebaiapi.OrderSkuDiscountTypeTe: 1, } deliveryTypeMap = map[int]string{ ebaiapi.DeliveryPartyFengElmSelf: model.OrderDeliveryTypeStoreSelf, } ) func mapDeliveryType(ebaiDeliveryParty int, businessType int) (deliveryType string) { if businessType == ebaiapi.DeliveryBusinessTypeZT { return model.OrderDeliveryTypeSelfTake } else { deliveryType = deliveryTypeMap[ebaiDeliveryParty] if deliveryType == "" { deliveryType = model.OrderDeliveryTypePlatform } } return deliveryType } func (p *PurchaseHandler) getStatusFromVendorStatus(vendorStatus string) int { if status, ok := VendorStatus2StatusMap[vendorStatus]; ok { return status } return model.OrderStatusUnknown } func (p *PurchaseHandler) GetOrder(vendorOrgCode, vendorOrderID, vendorStoreID string) (order *model.GoodsOrder, err error) { order, _, err = p.getOrder(vendorOrderID) return order, err } func (p *PurchaseHandler) GetOrderStatus(vendorOrgCode, vendorOrderID string) (status int, err error) { status, err = api.EbaiAPI.OrderStatusGet(vendorOrderID) if err == nil { status = p.getStatusFromVendorStatus(utils.Int2Str(status)) } return status, err } func (p *PurchaseHandler) getOrder(vendorOrderID string) (order *model.GoodsOrder, orderMap map[string]interface{}, err error) { for i := 0; i < 2; i++ { orderMap, err = api.EbaiAPI.OrderGet(vendorOrderID) if err == nil { order = p.Map2Order(orderMap) // 饿百订单有时会出现取不到baidu_shop_id的情况,重试 if order.VendorStoreID != "" { break } } time.Sleep(100 * time.Millisecond) } return order, orderMap, err } func (p *PurchaseHandler) GetOrder4PartRefund(vendorOrderID string) (order *model.GoodsOrder, err error) { taskIDs := []int{1, 2} var ( err1, err2 error result1, result2 map[string]interface{} ) task := tasksch.NewParallelTask("GetOrder4PartRefund", nil, jxcontext.AdminCtx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { taskID := batchItemList[0].(int) if taskID == 1 { result1, err1 = api.EbaiAPI.OrderGet(vendorOrderID) } else if taskID == 2 { result2, err2 = api.EbaiAPI.OrderPartRefundGet(vendorOrderID) } return nil, nil }, taskIDs) task.Run() task.GetResult(0) if err1 == nil { order = p.Map2Order(result1) if err2 == nil { skuHave := make([]*model.OrderSku, 0, 0) refundSkuList := p.partRefund2OrderDetailSkuList(utils.Interface2String(result2["order_id"]), result2["reverse_order_list"]) if len(refundSkuList) != model.NO { refundSkuMap := make(map[string]*model.OrderSku, 0) for _, rs := range refundSkuList { refundSkuMap[rs.VendorSkuID] = rs } for _, os := range order.Skus { if _, OK := refundSkuMap[os.VendorSkuID]; !OK { skuHave = append(skuHave, os) } else { if refundSkuMap[os.VendorSkuID].Count != os.Count { os.Count = os.Count - refundSkuMap[os.VendorSkuID].Count skuHave = append(skuHave, os) } } } } order.Skus = skuHave giftSkus, discountMoney := getZengSkus(vendorOrderID, result1) order.DiscountMoney = discountMoney order.Skus = append(order.Skus, giftSkus...) order.ActualPayPrice = utils.MustInterface2Int64(result2["remain_user_total_amount"]) order.TotalShopMoney = utils.MustInterface2Int64(result2["merchant_income"]) jxutils.RefreshOrderSkuRelated(order) } else if err2Ext, ok := err2.(*utils.ErrorWithCode); !ok || err2Ext.IntCode() != ebaiapi.ErrOrderIsNotPartRefund { err = err2 } } else { err = err1 } return order, err } func getZengSkus(orderID string, orderMan map[string]interface{}) (skus []*model.OrderSku, discountMoney int64) { discounts, _ := orderMan["discount"].([]interface{}) for _, v := range discounts { discount := v.(map[string]interface{}) discountType := utils.Interface2String(discount["type"]) if discountType == ebaiapi.OrderSkuDiscountTypeZeng { sku := &model.OrderSku{ VendorOrderID: orderID, VendorID: model.VendorIDEBAI, Count: 1, SkuID: 0, VendorSkuID: "", SkuName: utils.Interface2String(discount["desc"]), VendorPrice: 0, } skus = append(skus, sku) } discountMoney += utils.Interface2Int64WithDefault(discount["fee"], 0) } return skus, discountMoney } func (p *PurchaseHandler) partRefund2OrderDetailSkuList(orderID string, orderDetail2 interface{}) []*model.OrderSku { refundDetail := orderDetail2.([]interface{}) refundSkuList := refundDetail[len(refundDetail)-1].(map[string]interface{})["sub_reverse_order_list"].([]interface{}) skuList := make([]*model.OrderSku, 0, 0) for _, product2 := range refundSkuList { product := product2.(map[string]interface{}) if product["virtual_type"] == ebaiapi.OrderVirtualType { skuName := product["sku_name"].(string) _, _, _, specUnit, _, specQuality := jxutils.SplitSkuName(skuName) number := int(utils.MustInterface2Int64(product["apply_quantity"])) sku := &model.OrderSku{ VendorOrderID: orderID, VendorID: model.VendorIDEBAI, Count: number, SkuID: int(utils.Str2Int64WithDefault(utils.Interface2String(product[ebaiapi.KeyCustomSkuID]), 0)), VendorSkuID: utils.Int64ToStr(utils.Interface2Int64WithDefault(product["platform_sku_id"], 0)), SkuName: skuName, Weight: int(utils.Interface2Int64WithDefault(product["refund_weight"], 0)) / number, // 退单这里的total_weight有BUG,这里的total_weight还是没有退单时的值 VendorPrice: utils.MustInterface2Int64(product["refund_user_amount"]), } //sku.SalePrice, _, sku.StoreSubName = getSkuSalePrice(product) sku.SalePrice = utils.Interface2Int64WithDefault(product["discount_detail"].(map[string]interface{})["total_price"], 0) if sku.Weight == 0 { sku.Weight = jxutils.FormatSkuWeight(specQuality, specUnit) // 订单信息里没有重量,只有名字里尝试找 } skuList = append(skuList, sku) } } return skuList } func getExpectedDeliveredTime(orderMap map[string]interface{}) (expectedTime time.Time) { expectedTime = getTimeFromInterface(orderMap["latest_send_time"]) if utils.IsTimeZero(expectedTime) { expectedTime = getTimeFromInterface(orderMap["send_time"]) } return expectedTime } 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: utils.Interface2String(orderMap["eleme_order_id"]), VendorID: model.VendorIDEBAI, VendorStoreID: utils.Interface2String(shopMap["baidu_shop_id"]), StoreID: int(utils.Str2Int64WithDefault(utils.Interface2String(shopMap["id"]), 0)), StoreName: utils.Interface2String(shopMap["name"]), VendorUserID: utils.Interface2String(userMap["user_id"]), ConsigneeName: utils.Interface2String(userMap["name"]), ConsigneeMobile: jxutils.FormalizeMobile(utils.Interface2String(userMap["phone"])), ConsigneeAddress: utils.Interface2String(userMap["address"]), CoordinateType: model.CoordinateTypeMars, BuyerComment: utils.TrimBlankChar(utils.Interface2String(orderMap["remark"])), ExpectedDeliveredTime: getExpectedDeliveredTime(orderMap), PickDeadline: utils.DefaultTimeValue, VendorStatus: utils.Int64ToStr(utils.MustInterface2Int64(orderMap["status"])), OrderSeq: int(utils.ForceInterface2Int64(orderMap["order_index"])), StatusTime: getTimeFromInterface(orderMap["create_time"]), OrderCreatedAt: getTimeFromInterface(orderMap["create_time"]), // OrderFinishedAt: getTimeFromInterface(orderMap["finished_time"]), OriginalData: string(utils.MustMarshal(result)), ActualPayPrice: utils.ForceInterface2Int64(orderMap["user_fee"]), BaseFreightMoney: utils.ForceInterface2Int64(orderMap["send_fee"]), TotalShopMoney: utils.ForceInterface2Int64(orderMap["shop_fee"]), DeliveryType: mapDeliveryType(int(utils.ForceInterface2Int64(orderMap["delivery_party"])), int(utils.ForceInterface2Int64(orderMap["business_type"]))), InvoiceTitle: utils.Interface2String(orderMap["invoice_title"]), InvoiceTaxerID: utils.Interface2String(orderMap["taxer_id"]), InvoiceEmail: jxutils.GetOneEmailFromStr(utils.Interface2String(orderMap["invoice_address"])), VendorOrgCode: utils.Interface2String(result["source"]), } invoiceType, _ := utils.TryInterface2Int64(orderMap["invoice_type"]) switch invoiceType { case 2: // 纸质发票 order.InvoiceEmail += "(纸质发票)" case 4: // 电子发票 order.InvoiceEmail += "(电子发票)" default: // 不需要或者错误 } if utils.Interface2String(userMap["backupPhone"]) != "" { order.ConsigneeMobile2 = jxutils.FormalizeMobile(utils.Interface2String(userMap["backupPhone"])) } finishTime := getTimeFromInterface(orderMap["finished_time"]) if finishTime == utils.ZeroTimeValue { order.OrderFinishedAt = utils.DefaultTimeValue } else { order.OrderFinishedAt = finishTime } 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 order.Status >= model.OrderStatusEndBegin { order.OrderFinishedAt = time.Now() } 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_amap"].(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{}) for _, product2 := range products { product := product2.(map[string]interface{}) skuName := product["product_name"].(string) _, _, _, specUnit, _, specQuality := jxutils.SplitSkuName(skuName) productAmount := int(utils.MustInterface2Int64(product["product_amount"])) sku := &model.OrderSku{ VendorOrderID: order.VendorOrderID, VendorID: model.VendorIDEBAI, Count: productAmount, SkuID: int(utils.Str2Int64WithDefault(utils.Interface2String(product[ebaiapi.KeyCustomSkuID]), 0)), VendorSkuID: utils.Interface2String(product["baidu_product_id"]), SkuName: skuName, Weight: int(utils.Interface2Int64WithDefault(product["total_weight"], 0)) / productAmount, VendorPrice: utils.MustInterface2Int64(product["product_price"]), } if shelfPosition, ok := product["shelf_position"]; ok { sku.LocationCode = shelfPosition.(string) } if sku.SkuID == 0 { if product["upc"] != nil && len(strings.Split(product["upc"].(string), "-")) > 1 { sku.SkuID = utils.Str2Int(strings.Split(product["upc"].(string), "-")[1]) } else if product["baidu_product_id"] != nil { skuBind, err := dao.GetStoreSkuByVendorSkuId(order.StoreID, model.VendorIDEBAI, product["baidu_product_id"].(string)) if err == nil && skuBind != nil { sku.SkuID = skuBind.SkuID } } } var baiduRate int64 sku.SalePrice, baiduRate, sku.StoreSubName = getSkuSalePrice(product) order.PmSubsidyMoney += baiduRate if sku.Weight == 0 { sku.Weight = jxutils.FormatSkuWeight(specQuality, specUnit) // 订单信息里没有重量,只有名字里尝试找 } // if product["isGift"].(bool) { // sku.SkuType = 1 // } order.Skus = append(order.Skus, sku) } giftSkus, discountMoney := getZengSkus(vendorOrderID, orderData) order.DiscountMoney = discountMoney order.Skus = append(order.Skus, giftSkus...) // 包装袋金额+服务费 store, _ := dao.GetStoreDetailByVendorStoreID(dao.GetDB(), order.VendorStoreID, model.VendorIDEBAI, order.VendorOrgCode) if store != nil { order.PackagePrice = store.PackageSetting } jxutils.RefreshOrderSkuRelated(order) return order } func getSkuSalePrice(product map[string]interface{}) (salePrice, baiduRate int64, vendorActType string) { var product2 *ebaiapi.OrderProductInfo if err := utils.Map2StructByJson(product, &product2, true); err != nil { return utils.MustInterface2Int64(product["product_price"]), 0, "" } return getSkuSalePrice2(product2) } func getSkuSalePrice2(product *ebaiapi.OrderProductInfo) (salePrice, baiduRate int64, vendorActType string) { salePrice = int64(product.ProductPrice) if product.ProductSubsidy != nil { for _, v := range product.ProductSubsidy.DiscountDetail { if skuActTypeMap[v.Type] == 1 { skuCount := product.ProductAmount if skuCount == 0 { skuCount = product.Number } salePrice -= int64(math.Round(float64(v.BaiduRate+v.ShopRate) / float64(skuCount))) // 饿百同一SKU的优惠与非优惠没有拆开,平均摊销处理 vendorActType = v.Type } baiduRate += int64(v.BaiduRate) } } return salePrice, baiduRate, vendorActType } func (p *PurchaseHandler) AcceptOrRefuseOrder(order *model.GoodsOrder, isAcceptIt bool, userName string) (err error) { if isAcceptIt { if globals.EnableEbaiStoreWrite { err = api.EbaiAPI.OrderConfirm(order.VendorOrderID) store, _ := dao.GetStoreDetail(dao.GetDB(), order.JxStoreID, order.VendorID, "") flag := store.AutoPickup == model.YES && store.VendorStatus == model.StoreStatusOpened // 只要打开了就可以自动拣货 if flag { utils.AfterFuncWithRecover(time.Minute*6, func() { err = api.EbaiAPI.OrderPickComplete(order.VendorOrderID) }) } //if beego.BConfig.RunMode == model.ServerTypeFruits || beego.BConfig.RunMode == model.ServerTypePet { // utils.AfterFuncWithRecover(time.Minute*10, func() { // err = api.EbaiAPI.OrderPickComplete(order.VendorOrderID) // }) //} else { // utils.AfterFuncWithRecover(time.Minute, func() { // err = api.EbaiAPI.OrderPickComplete(order.VendorOrderID) // }) //} } p.postFakeMsg(order.VendorOrderID, fakeAcceptOrder) } else { if globals.EnableEbaiStoreWrite { //err = api.EbaiAPI.OrderCancel(order.VendorOrderID, ebaiapi.CancelTypeCustom, "bu") err = api.EbaiAPI.OrderReverseApply(&ebaiapi.OrderReverseApplyParam{ OrderId: order.VendorOrderID, IdempotentId: utils.Int64ToStr(time.Now().Unix()), RefundType: ebaiapi.MerchantOrderReverseApplyCancel, ReasonCode: "7015", ReasonRemarks: "", NeedIvrUser: 0, RefundProductList: nil, }) } } return err } func (p *PurchaseHandler) PickupGoods(order *model.GoodsOrder, isSelfDelivery bool, userName string) (err error) { if globals.EnableEbaiStoreWrite { if userName == "autoPickup" && !isSelfDelivery { return nil } err = api.EbaiAPI.OrderPickComplete(order.VendorOrderID) } if err == nil { 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) { if globals.EnableEbaiStoreWrite { deliveryStatusObj, _ := api.EbaiAPI.OrderDeliveryGet(order.VendorOrderID) deliveryStatus := utils.Int64ToStr(utils.MustInterface2Int64(deliveryStatusObj["status"])) switch deliveryStatus { case ebaiapi.WaybillStatusSelfDelivery: err = nil case ebaiapi.WaybillStatusDeliveryCancled: p.trySyncCancelStatus(order.VendorOrderID) case ebaiapi.WaybillStatusNew, ebaiapi.WaybillStatusEvent, ebaiapi.WaybillStatusRequestDelivery, ebaiapi.WaybillStatusWait4Courier: err = api.EbaiAPI.OrderCancelDelivery(order.VendorOrderID) // 取消呼叫众包骑手 case ebaiapi.WaybillStatusCourierAccepted: // 骑手已接单,无法转自送 err = nil } } if err == nil { // 饿百不会发送配送中,模拟发送 p.postFakeMsg(order.VendorOrderID, ebaiapi.OrderStatusCourierAccepted) } return err } func (p *PurchaseHandler) trySyncCancelStatus(vendorOrderID string) (err error) { orderInfo, err := api.EbaiAPI.OrderGet2(vendorOrderID) if err == nil { if utils.Int2Str(orderInfo.Order.Status) == ebaiapi.OrderStatusCanceled { p.postFakeMsg(vendorOrderID, fakeOrderCanceled) } } return err } // 将订单从购物平台配送转为自送后又送达 func (p *PurchaseHandler) Swtich2SelfDelivered(order *model.GoodsOrder, userName string) (err error) { param2 := &ebaiapi.PushRiderInfo{ DistributorId: 201, OrderId: order.VendorOrderID, State: 21, SelfStatus: ebaiapi.OrderDeliveryStatusSendOver, SelfStatusDesc: "自配送送达", DistributorInfoDTO: ebaiapi.DistributorInfoDTO{ DistributorTypeId: "99999", DistributorName: "商家自行配送", }, } bills, _ := dao.GetWaybills(dao.GetDB(), order.VendorOrderID, nil) for _, v := range bills { if v.Status == model.WaybillStatusDelivered || v.Status == model.WaybillStatusFailed { param2.Knight = ebaiapi.Knight{ Id: utils.Str2Int64(order.VendorOrderID), Name: v.CourierName, Phone: v.CourierMobile, } break } } if param2.Knight.Name == "" { store, _ := dao.GetStoreDetail(dao.GetDB(), order.JxStoreID, order.VendorID, order.VendorOrgCode) if store != nil { param2.Knight = ebaiapi.Knight{ Id: utils.Str2Int64(order.VendorOrderID), Name: store.Name, Phone: store.Tel1, } } } err = api.EbaiAPI.OrderselfDeliveryStateSync2(param2) return err } // 完全自送的门店表示开始配送 func (p *PurchaseHandler) SelfDeliverDelivering(order *model.GoodsOrder, userName string) (err error) { if globals.EnableEbaiStoreWrite { param2 := &ebaiapi.PushRiderInfo{ DistributorId: 201, OrderId: order.VendorOrderID, State: 21, SelfStatus: 1, SelfStatusDesc: "生成自配送运单", DistributorInfoDTO: ebaiapi.DistributorInfoDTO{ DistributorTypeId: "99999", DistributorName: "商家自行配送", }, } err = api.EbaiAPI.OrderselfDeliveryStateSync2(param2) } if err == nil { if order.VendorID == model.VendorIDEBAI && strings.Contains(order.ConsigneeAddress, "隐私保护") { // 部分订单在隐私条约的保护下会隐藏用户收货地址,转自送成功自后才可以获取到正确的地址. orderMap, err := api.EbaiAPI.OrderGet(order.VendorOrderID) if err == nil { userMap := orderMap["user"].(map[string]interface{}) order.ConsigneeAddress = utils.Interface2String(userMap["address"]) partner.CurOrderManager.UpdateOrderFields(order, []string{"ConsigneeAddress"}) } } // 饿百不会发送配送中,模拟发送 p.postFakeMsg(order.VendorOrderID, ebaiapi.OrderStatusCourierAccepted) } return err } // 完全自送的门店表示配送完成 func (p *PurchaseHandler) SelfDeliverDelivered(order *model.GoodsOrder, userName string) (err error) { if globals.EnableEbaiStoreWrite { param2 := &ebaiapi.PushRiderInfo{ DistributorId: 201, OrderId: order.VendorOrderID, State: 21, SelfStatus: ebaiapi.OrderDeliveryStatusSendOver, SelfStatusDesc: "自配送送达", DistributorInfoDTO: ebaiapi.DistributorInfoDTO{ DistributorTypeId: "99999", DistributorName: "商家自行配送", }, } bills, _ := dao.GetWaybills(dao.GetDB(), order.VendorOrderID, nil) for _, v := range bills { if v.Status == model.WaybillStatusDelivered || v.Status == model.WaybillStatusFailed { param2.Knight = ebaiapi.Knight{ Id: utils.Str2Int64(order.VendorOrderID), Name: v.CourierName, Phone: v.CourierMobile, } break } } err = api.EbaiAPI.OrderselfDeliveryStateSync2(param2) } return err } // func (c *PurchaseHandler) onOrderMsg(msg *ebaiapi.CallbackMsg) (retVal *ebaiapi.CallbackResponse) { if c.isAfsMsg(msg) { retVal = c.onAfsOrderMsg(msg) } else { status := c.callbackMsg2Status(msg) //if partner.CurOrderManager.GetStatusDuplicatedCount(status) > 0 { // return nil //} if ebaiapi.CmdOrderCreate == msg.Cmd { retVal = c.onOrderNew(msg, status) } else { var err error if status != nil { if status.Status == model.OrderStatusAdjust { order, err := c.GetOrder4PartRefund(GetOrderIDFromMsg(msg)) if err == nil { err = partner.CurOrderManager.OnOrderAdjust(order, status) } if order.OrderFinishedAt != utils.ZeroTimeValue { partner.CurOrderManager.UpdateOrderFields(order, []string{"OrderFinishedAt"}) } } else { // 处理饿百降级订单的情况 // 是否降级;1:是,0:否;极少数订单因网络或信息交互异常,导致订单部分字段(如订单金额)生成延迟,此时订单会被标记为“已降级”状态,需开发者重新调用查看订单详情接口获取完整订单数据。 // toto sku是否也需要处理? if status.Status == model.OrderStatusDelivering || status.Status == model.OrderStatusFinished { if order, err2 := partner.CurOrderManager.LoadOrder(status.VendorOrderID, status.VendorID); err2 == nil { if order.TotalShopMoney == 0 { if order2, err2 := c.GetOrder(msg.Source, status.VendorOrderID, ""); err2 == nil { order.TotalShopMoney = order2.TotalShopMoney order.PmSubsidyMoney = order2.PmSubsidyMoney partner.CurOrderManager.UpdateOrderFields(order, []string{"TotalShopMoney", "PmSubsidyMoney"}) } } if status.Status == model.OrderStatusFinished { order.OrderFinishedAt = time.Now() partner.CurOrderManager.UpdateOrderFields(order, []string{"OrderFinishedAt"}) } } } err = partner.CurOrderManager.OnOrderStatusChanged(msg.Source, status) } } retVal = api.EbaiAPI.Err2CallbackResponse(msg.Cmd, err, nil) } } return retVal } func (c *PurchaseHandler) onOrderNew(msg *ebaiapi.CallbackMsg, orderStatus *model.OrderStatus) (response *ebaiapi.CallbackResponse) { vendorOrderID := GetOrderIDFromMsg(msg) order, orderMap, err := c.getOrder(vendorOrderID) if err == nil { // 饿百订单有时会出现取不到baidu_shop_id的情况,返回错误让服务器重试 if order.VendorStoreID == "" { return api.EbaiAPI.Err2CallbackResponse(msg.Cmd, fmt.Errorf("订单%s的baidu_shop_id为空", order.VendorOrderID), "") } if err = partner.CurOrderManager.OnOrderNew(order, orderStatus); err == nil { utils.CallFuncAsync(func() { c.OnOrderDetail(orderMap, partner.CreatedPeration) }) } } if handler := partner.GetPurchaseOrderHandlerFromVendorID(model.VendorIDEBAI); handler != nil { handler.AcceptOrRefuseOrder(order, true, "jxAdmin") } 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 := int(utils.MustInterface2Int64(msg.Body["type"])) cancelType := int(utils.MustInterface2Int64(msg.Body["cancel_type"])) orderStatus.Remark = buildFullReason(utils.Interface2String(msg.Body["cancel_reason"]), utils.Interface2String(msg.Body["addition_reason"])) orderStatus.VendorStatus = msg.Cmd + "-" + utils.Int2Str(msgType) if cancelType == ebaiapi.OrderUserCancelTypeBeforeSale { // 完成前取消 if msgType == ebaiapi.OrderUserCancelApply /* || msgType == ebaiapi.OrderUserCancelCSIntervene */ { orderStatus.Status = model.OrderStatusApplyCancel } else if msgType == ebaiapi.OrderUserCancelCSRefused || msgType == ebaiapi.OrderUserCancelMerchantRefused { orderStatus.Status = model.OrderStatusVendorRejectCancel } else if msgType == ebaiapi.OrderUserCancelInvalid { orderStatus.Status = model.OrderStatusUndoApplyCancel } else if msgType == ebaiapi.OrderUserCancelCSAgreed || msgType == ebaiapi.OrderUserCancelMerchantAgreed { orderStatus.Status = model.OrderStatusVendorAgreeCancel } } } else if msg.Cmd == ebaiapi.CmdOrderPartRefund { msgType := int(utils.MustInterface2Int64(msg.Body["type"])) status := int(utils.MustInterface2Int64(msg.Body["status"])) orderStatus.Remark = buildFullReason(utils.Interface2String(msg.Body["reason"]), utils.Interface2String(msg.Body["addition_reason"])) if msgType == ebaiapi.OrderPartRefuncTypeMerchant && status == ebaiapi.OrderPartRefundSuccess { orderStatus.VendorStatus = fakeOrderAdjustFinished } } else if msg.Cmd == ebaiapi.CmdOrderReversePush { msgType := int(utils.MustInterface2Int64(msg.Body["operator_role"])) status := int(utils.MustInterface2Int64(msg.Body["refund_status"])) orderStatus.Remark = buildFullReason(utils.Interface2String(msg.Body["reason"]), utils.Interface2String(msg.Body["addition_reason"])) if msgType == ebaiapi.OrderReverseRefuncTypeMerchant && status == ebaiapi.OrderReversePushApplySuccess { 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"]) } if orderStatus.Status == 0 { orderStatus.Status = c.getStatusFromVendorStatus(orderStatus.VendorStatus) } return orderStatus } func buildFullReason(reason, addReason string) (fullReason string) { fullReason = reason if addReason != "" { fullReason += ",额外原因:" + addReason } return fullReason } 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, isAcceptIt bool, reason string) (err error) { if globals.EnableEbaiStoreWrite { param := &ebaiapi.RefundOrderExamine{ ReverseOrderId: "", OrderId: order.VendorOrderID, IdempotentId: utils.Int64ToStr(time.Now().UnixNano()), ReasonRemarks: reason, } refundProductList := make([]*ebaiapi.RefundProductList, 0, 0) date, _, _ := api.EbaiAPI.GetReverseOrder(order.VendorOrderID) for _, v := range date { v2 := v.(map[string]interface{}) refundProduct := &ebaiapi.RefundProductList{ SubBizOrderId: utils.Interface2String(v2["sub_biz_order_id"]), PlatformSkuId: utils.Int64ToStr(utils.Interface2Int64WithDefault(v2["platform_sku_id"], 0)), } switch utils.MustInterface2Int64(v2["fund_calculate_type"]) { case 0: refundProduct.Number = utils.Int64ToStr(utils.MustInterface2Int64(v2["refund_quantity"])) case 1: refundProduct.RefundAmount = utils.Int64ToStr(utils.MustInterface2Int64(v2["refund_user_amount"])) } refundProductList = append(refundProductList, refundProduct) if param.ReverseOrderId == "" { param.ReverseOrderId = utils.Int64ToStr(utils.Interface2Int64WithDefault(v2["refund_order_id"], 0)) } } param.RefundProductList = refundProductList if isAcceptIt { param.ActionType = ebaiapi.RefundTypeAgree err = api.EbaiAPI.OrderAgreeRefund(param) } else { if reason == "" { return fmt.Errorf("拒绝退单时,请填写拒单原因") } param.ReasonCode = "7001" param.ActionType = ebaiapi.RefundTypeRefuse err = api.EbaiAPI.OrderDisagreeRefund(param) } } return err } func (c *PurchaseHandler) CancelOrder(ctx *jxcontext.Context, order *model.GoodsOrder, reason string) (err error) { if globals.EnableEbaiStoreWrite { err = api.EbaiAPI.OrderReverseApply(&ebaiapi.OrderReverseApplyParam{ OrderId: order.VendorOrderID, IdempotentId: utils.Int64ToStr(time.Now().Unix()), RefundType: ebaiapi.MerchantOrderReverseApplyCancel, ReasonCode: "7001", // 其他原因 ReasonRemarks: reason, NeedIvrUser: 0, RefundProductList: nil, }) //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) { // 饿百必须要确认订单后才能调整单 if order.Status < model.OrderStatusFinishedPickup { err = c.PickupGoods(order, false, ctx.GetUserName()) } if err == nil { //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) //} eabiApi := api.EbaiAPI orderDetail, err := eabiApi.OrderGet2(order.VendorOrderID) if err != nil { return err } refundSkuList := make([]*ebaiapi.RefundProductListParam, 0, 0) for _, r := range removedSkuList { for _, v := range orderDetail.Products[0] { if utils.Int2Str(jxutils.GetSkuIDFromOrderSku(r)) == v.CustomSkuID || r.VendorSkuID == v.BaiduProductID { refundSkuList = append(refundSkuList, &ebaiapi.RefundProductListParam{ SubBizOrderId: v.SubBizOrderID, PlatformSkuId: v.BaiduProductID, Number: utils.Int2Str(r.Count), RefundAmount: "", FundCalculateType: "0", }) } } } // 部分退款 err = eabiApi.OrderReverseApply(&ebaiapi.OrderReverseApplyParam{ OrderId: order.VendorOrderID, IdempotentId: utils.Int64ToStr(time.Now().Unix()), RefundType: ebaiapi.MerchantOrderReverseApplyPartCancel, ReasonCode: "7015", ReasonRemarks: reason, NeedIvrUser: 1, RefundProductList: refundSkuList, }) } return err } func (c *PurchaseHandler) ListOrders(ctx *jxcontext.Context, vendorOrgCode string, parentTask tasksch.ITask, queryDate time.Time, vendorStoreID string) (vendorOrderIDs []string, err error) { if utils.IsTimeZero(queryDate) { return nil, fmt.Errorf("queryDate必须指定") } fromDate := utils.Time2Date(queryDate) toDate := fromDate.Add(24*time.Hour - 1) var vendorStoreIDs []string if vendorStoreID == "" { vendorStoreIDs, err = c.GetAllStoresVendorID(ctx, "") if err != nil { return nil, err } } else { vendorStoreIDs = []string{vendorStoreID} } task := tasksch.NewParallelTask("ebai ListOrders", nil, ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { vendorStoreID := batchItemList[0].(string) orderList, err := api.EbaiAPI.OrderListAll("", utils.Str2Int64(vendorStoreID), fromDate.Unix(), toDate.Unix(), 0) if err == nil { retVal = orderList } return retVal, err }, vendorStoreIDs) tasksch.HandleTask(task, parentTask, true).Run() orderList, err := task.GetResult(0) if err == nil && len(orderList) > 0 { vendorOrderIDs = make([]string, len(orderList)) for k, v := range orderList { orderInfo := v.(*ebaiapi.ListOrderItemInfo) vendorOrderIDs[k] = orderInfo.OrderID } } return vendorOrderIDs, err } func (c *PurchaseHandler) GetWaybillTip(ctx *jxcontext.Context, vendorOrgCode, vendorStoreID, vendorOrderID, vendorWaybillID, vendorWaybillID2 string) (tipFee int64, err error) { orderInfo, err := api.EbaiAPI.GetStoreOrderInfo(vendorOrderID) if err == nil { if orderBasic, _ := orderInfo["order_basic"].(map[string]interface{}); orderBasic != nil { tipFee = jxutils.StandardPrice2Int(utils.Interface2Float64WithDefault(orderBasic["delivery_tip_amount"], 0)) } } return tipFee, err } func (c *PurchaseHandler) UpdateWaybillTip(ctx *jxcontext.Context, vendorOrgCode, vendorStoreID, vendorOrderID, vendorWaybillID, vendorWaybillID2, cityCode string, tipFee int64) (err error) { if globals.EnableEbaiStoreWrite { err = api.EbaiAPI.ModifyTip4OrderWaybill(vendorOrderID, "", jxutils.IntPrice2Standard(tipFee), 0) if err != nil { if strings.Contains(err.Error(), "HTTP code is not 200") { return fmt.Errorf("饿百此订单暂不支持加小费!") } } } return err } func (c *PurchaseHandler) GetSelfTakeCode(ctx *jxcontext.Context, order *model.GoodsOrder) (selfTakeCode string, err error) { // orderTrackList, err := api.EbaiAPI.OrderQueryAcceptancecode(order.VendorOrderID) // if err == nil { // for _, v := range orderTrackList { // if v.TagCode == 180 { // searchResult := selfTakeCodeReg.FindStringSubmatch(v.MsgContent) // if searchResult != nil && len(searchResult[1]) > 0 { // selfTakeCode = searchResult[1] // } // break // } // } // } return selfTakeCode, err } func (c *PurchaseHandler) ConfirmSelfTake(ctx *jxcontext.Context, order *model.GoodsOrder, selfTakeCode string) (err error) { if globals.EnableEbaiStoreWrite { err = api.EbaiAPI.OrderCheckout(order.VendorOrderID, selfTakeCode) } return err } func (c *PurchaseHandler) ComplaintRider(vendorOrderId string, resonID int, resonContent string) (err error) { return fmt.Errorf("饿了么暂无投诉骑手API") } // 转自配送时取消非专送混合送门店取消理由 func (c *PurchaseHandler) GetCancelDeliveryReason(order *model.GoodsOrder) (string, error) { return "", nil } // 取消美团外卖理由转使用三方配送 func (c *PurchaseHandler) CancelLogisticsByWmOrderId(order *model.GoodsOrder, reasonCode, detailContent, appPoiCode, orderId string) error { return nil } // 获取订单配送状态 func (c *PurchaseHandler) OrderLogisticsStatus(orderId string) (*utils.RiderInfo, error) { status, err := api.EbaiAPI.OrderDeliveryGet(orderId) if err != nil { return nil, err } data := &utils.RiderInfo{} data.LogisticsStatus = int(utils.MustInterface2Int64(status["status"])) data.CourierName = utils.Interface2String(status["name"]) data.CourierPhone = utils.Interface2String(status["phone"]) data.OrderId = utils.Interface2String(status["order_id"]) if status["latitude"] != nil { data.Latitude = utils.Float64ToStr(utils.MustInterface2Float64(status["latitude"])) } if status["longitude"] != nil { data.Longitude = utils.Float64ToStr(utils.MustInterface2Float64(status["longitude"])) } return data, nil } // GetOrderSettleAccounts 获取订单结算信息 func (c *PurchaseHandler) GetOrderSettleAccounts(order *model.GoodsOrder) (int64, error) { orderInfo, err := api.EbaiAPI.OrderGet(order.VendorOrderID) if err != nil { return 0, err } orderMap := orderInfo["order"].(map[string]interface{}) return utils.ForceInterface2Int64(orderMap["shop_fee"]), nil } // GetPlatformLogisticsFee 获取自配送订单的配送费 func (c *PurchaseHandler) GetPlatformLogisticsFee(order *model.GoodsOrder) (int64, error) { return 0, nil } // ApplyCompensationOrder 订单索赔 func (c *PurchaseHandler) ApplyCompensationOrder(order *model.GoodsOrder) (string, error) { afsOrderList, err := dao.GetAfsOrders(dao.GetDB(), order.VendorID, order.VendorOrderID, "") if err != nil { return "", err } refundList := make([]*ebaiapi.ApplyCompensationList, 0, 0) for _, v := range afsOrderList { if v.AfsOrderID != v.VendorOrderID { refundList = append(refundList, &ebaiapi.ApplyCompensationList{ RefundId: v.AfsOrderID, CompensationReason: ebaiapi.CompensationCodeE1, Description: "分配骑手缓慢/骑手到店缓慢", }) } } if len(refundList) > 0 { result, err := api.EbaiAPI.ApplyCompensation(order.VendorOrderID, refundList) if err != nil { return "", err } compensationStation := make([]string, 0, 0) for _, r := range result { compensationStation = append(compensationStation, fmt.Sprintf("售后ID:%s,索赔状态:%s,失败:%s", r.RefundId, r.CompensationStatusDesc, r.CompensationFailDesc)) } return strings.Join(compensationStation, ","), nil } return "", err } // UploadInvoice 回复用户发票申请 func (c *PurchaseHandler) UploadInvoice(param *model.InvoiceMsg, base64 string) ([]string, []string, error) { goodsOrder, err := partner.CurOrderManager.LoadOrder(param.OrderId, param.VendorID) if err != nil { return nil, nil, err } result, err := api.EbaiAPI.UploadInvoice(goodsOrder.VendorStoreID, param.InvoiceTaskId, param.InvoiceUrl) if err != nil { return nil, nil, err } return result.SuccessfulApplicationNoList, result.FailedApplicationNoList, nil }