Files
jx-callback/business/partner/purchase/ebai/order.go
gazebo 629247eba1 - fk
2019-03-26 16:46:04 +08:00

386 lines
15 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 = 180 * time.Second
pickupOrderDelay = 240 * time.Second
pickupOrderGap = 30
// fakePickedUp = "9527"
fakeUserApplyCancel = "user_apply_cancel"
fakeAcceptOrder = "accept_order"
)
// 饿百的接单会直接召唤配送,为了统一将饿百的接单影射成拣货完成,然后模拟一个接单消息
var (
VendorStatus2StatusMap = map[string]int{
ebaiapi.OrderStatusNew: model.OrderStatusNew,
fakeAcceptOrder: model.OrderStatusAccepted,
ebaiapi.OrderStatusAccepted: model.OrderStatusFinishedPickup,
fakeUserApplyCancel: model.OrderStatusApplyCancel,
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) {
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: 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 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 isAcceptIt {
p.postFakeOrderAcceptMsg(order.VendorOrderID)
} else {
if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.OrderCancel(order.VendorOrderID, ebaiapi.CancelTypeCustom, "bu")
}
}
return err
}
func (p *PurchaseHandler) PickupGoods(order *model.GoodsOrder, isSelfDeilivery bool, userName string) (err error) {
globals.SugarLogger.Debugf("ebai PickupGoods orderID:%s", order.VendorOrderID)
if globals.EnableStoreWrite && globals.EnableEbaiStoreWrite {
if !isSelfDeilivery {
err = api.EbaiAPI.OrderCallDelivery(order.VendorOrderID)
} else {
err = api.EbaiAPI.OrderConfirm(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.postFakeOrderAcceptMsg(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, orderMap, err := c.getOrder(vendorOrderID)
if err == nil {
if err = partner.CurOrderManager.OnOrderNew(order, order.VendorStatus); err == nil {
utils.CallFuncAsync(func() {
c.OnOrderDetail(orderMap)
})
}
}
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),
}
if msg.Cmd == ebaiapi.CmdOrderUserCancel {
cancelType := utils.Int64ToStr(utils.MustInterface2Int64(msg.Body["type"]))
if cancelType == ebaiapi.OrderUserApplyCancel {
orderStatus.VendorStatus = fakeUserApplyCancel
orderStatus.Status = c.GetStatusFromVendorStatus(orderStatus.VendorStatus)
orderStatus.Remark = utils.Interface2String(msg.Body["cancel_reason"])
} else {
orderStatus = nil
}
} else if status, ok := msg.Body["status"]; ok {
vendorStatus := utils.Int64ToStr(utils.MustInterface2Int64(status))
orderStatus.VendorStatus = vendorStatus
orderStatus.Status = c.GetStatusFromVendorStatus(orderStatus.VendorStatus)
orderStatus.Remark = utils.Interface2String(msg.Body["reason"])
} else {
orderStatus = nil
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) (params *partner.StatusActionParams) {
if statusType == scheduler.TimerStatusTypeOrder && status == model.OrderStatusAccepted {
params = &partner.StatusActionParams{ // 饿百要求在5分钟内拣货不然订单会被取消
Timeout: pickupOrderDelay,
TimeoutGap: pickupOrderGap,
}
}
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) postFakeOrderAcceptMsg(vendorOrderID string) {
msg := &ebaiapi.CallbackMsg{
Cmd: ebaiapi.CmdOrderStatus,
Timestamp: time.Now().Unix(),
Body: map[string]interface{}{
"status": json.Number(fakeAcceptOrder), // json.Number实际是string
"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而不是空01538103149不是特殊值只是一个任意之前的时间这样写可以处理
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,
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 {
if !jxutils.IsMobileFake(mobile) && strings.Index(order.ConsigneeMobile, mobile) == -1 {
order.ConsigneeMobile2 = mobile
_, err = dao.UpdateEntity(db, order, "ConsigneeMobile2")
}
} else {
globals.SugarLogger.Infof("RefreshRealMobile orderID:%s failed with error:%v", order.VendorOrderID, err)
}
return nil, err
}, orderList)
tasksch.HandleTask(task, nil, true).Run()
hint = task.ID
if !isAsync {
_, err = task.GetResult(0)
}
}
return hint, err
}