Files
jx-callback/business/partner/purchase/elm/order.go
gazebo 79ed0a7929 - cancel waybill reason added.
- api operator added.
- token added.
2018-08-22 16:06:47 +08:00

306 lines
12 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 elm
import (
"fmt"
"math"
"strings"
"time"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
"git.rosy.net.cn/baseapi/platformapi/elmapi"
"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/model"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/legacy/freshfood"
)
const (
acceptOrderDelay = 270 * time.Second
fakePickedUp = "fakefinishedpickup"
)
var (
VendorStatus2StatusMap = map[string]int{
elmapi.OrderStatusUnprocessed: model.OrderStatusNew,
elmapi.OrderStatusValid: model.OrderStatusAccepted,
elmapi.OrderStatusPending: model.OrderStatusNew,
elmapi.OrderStatusRefunding: model.OrderStatusApplyRefund,
elmapi.OrderStatusInvalid: model.OrderStatusCanceled,
elmapi.OrderStatusSettled: model.OrderStatusFinished,
}
)
func (c *PurchaseHandler) OnOrderStatusMsg(msg *elmapi.CallbackOrderStatusMsg) (retVal *elmapi.CallbackResponse) {
jxutils.CallMsgHandler(func() {
retVal = c.onOrderStatusMsg(msg)
}, jxutils.ComposeUniversalOrderID(msg.OrderID, model.VendorIDELM))
return retVal
}
func (c *PurchaseHandler) OnOrderNewMsg(msg map[string]interface{}) (retVal *elmapi.CallbackResponse) {
jxutils.CallMsgHandler(func() {
retVal = c.onOrderNew(msg)
}, jxutils.ComposeUniversalOrderID(msg["orderId"].(string), model.VendorIDELM))
return retVal
}
func (c *PurchaseHandler) OnOrderCancelRefundMsg(msg *elmapi.CallbackOrderCancelRefundMsg) (retVal *elmapi.CallbackResponse) {
jxutils.CallMsgHandler(func() {
retVal = c.onOrderCancelRefundMsg(msg)
}, jxutils.ComposeUniversalOrderID(msg.OrderID, model.VendorIDELM))
return retVal
}
func (c *PurchaseHandler) orderStatusMsg2Status(msg *elmapi.CallbackOrderStatusMsg) *model.OrderStatus {
orderStatus := &model.OrderStatus{
VendorOrderID: msg.OrderID,
VendorID: model.VendorIDELM,
OrderType: model.OrderTypeOrder,
RefVendorOrderID: msg.OrderID,
RefVendorID: model.VendorIDELM,
VendorStatus: c.stateAndType2Str(msg.State, msg.MsgType),
StatusTime: utils.Timestamp2Time(msg.UpdateTime),
}
return orderStatus
}
func (c *PurchaseHandler) cancelRefundMsg2Status(msg *elmapi.CallbackOrderCancelRefundMsg) *model.OrderStatus {
orderStatus := &model.OrderStatus{
VendorOrderID: msg.OrderID,
VendorID: model.VendorIDELM,
OrderType: model.OrderTypeOrder,
RefVendorOrderID: msg.OrderID,
RefVendorID: model.VendorIDELM,
VendorStatus: c.stateAndType2Str(msg.RefundStatus, msg.MsgType),
StatusTime: utils.Timestamp2Time(msg.UpdateTime),
}
return orderStatus
}
func (c *PurchaseHandler) onOrderStatusMsg(msg *elmapi.CallbackOrderStatusMsg) (retVal *elmapi.CallbackResponse) {
status := c.orderStatusMsg2Status(msg)
switch msg.MsgType {
case elmapi.MsgTypeOrderAccepted:
status.Status = model.OrderStatusAccepted
case elmapi.MsgTypeOrderCanceled, elmapi.MsgTypeOrderInvalid, elmapi.MsgTypeOrderForceInvalid:
status.Status = model.OrderStatusCanceled
case elmapi.MsgTypeOrderFinished:
status.Status = model.OrderStatusFinished
default:
globals.SugarLogger.Warnf("onOrderStatusMsg elm msg:%v not handled", msg)
return elmapi.SuccessResponse
}
err := partner.CurOrderManager.OnOrderStatusChanged(status)
// 直接跳到拣货完成
if msg.MsgType == elmapi.MsgTypeOrderAccepted {
status.Status = model.OrderStatusFinishedPickup
status.VendorStatus = fakePickedUp
err = partner.CurOrderManager.OnOrderStatusChanged(status)
}
// if globals.HandleLegacyJxOrder && err == nil {
// c.legacyElmOrderStatusChanged(status)
// }
return elmapi.Err2CallbackResponse(err, status.VendorStatus)
}
func (c *PurchaseHandler) onOrderCancelRefundMsg(msg *elmapi.CallbackOrderCancelRefundMsg) (retVal *elmapi.CallbackResponse) {
status := c.cancelRefundMsg2Status(msg)
switch msg.MsgType {
case elmapi.MsgTypeUserApplyCancel:
status.Status = model.OrderStatusApplyCancel
case elmapi.MsgTypeUserApplyRefund:
status.Status = model.OrderStatusApplyRefund
default:
status.Status = model.OrderStatusUnknown
}
return elmapi.Err2CallbackResponse(partner.CurOrderManager.OnOrderStatusChanged(status), status.VendorStatus)
}
func (c *PurchaseHandler) GetOrder(orderID string) (order *model.GoodsOrder, err error) {
result, err := api.ElmAPI.GetOrder(orderID)
if err == nil {
phoneList := result["phoneList"].([]interface{})
consigneeMobile := ""
if len(phoneList) > 0 {
consigneeMobile = utils.Interface2String(phoneList[0])
}
// globals.SugarLogger.Debug(result)
order = &model.GoodsOrder{
VendorOrderID: orderID,
VendorID: model.VendorIDELM,
VendorStoreID: utils.Int64ToStr(utils.MustInterface2Int64(result["shopId"])),
StoreID: int(utils.Str2Int64WithDefault(utils.Interface2String(result["openId"]), 0)),
StoreName: result["shopName"].(string),
ConsigneeName: utils.FilterMb4(result["consignee"].(string)),
ConsigneeMobile: consigneeMobile,
ConsigneeAddress: utils.FilterMb4(result["address"].(string)),
BuyerComment: utils.FilterMb4(strings.Trim(utils.Interface2String(result["description"]), "\n\r\t ")),
ExpectedDeliveredTime: utils.Str2TimeWithDefault(utils.Interface2String(result["deliverTime"]), utils.DefaultTimeValue),
VendorStatus: utils.Interface2String(result["status"]), // 取订单的原始status不合并消息类型因为当前消息类型没有意义
OrderSeq: int(utils.MustInterface2Int64(result["daySn"])),
StatusTime: utils.Str2Time(result["activeAt"].(string)),
OriginalData: utils.FilterMb4(string(utils.MustMarshal(result))),
ActualPayPrice: jxutils.StandardPrice2Int(utils.MustInterface2Float64(result["totalPrice"])),
Skus: []*model.OrderSku{},
}
order.Status = c.GetStatusFromVendorStatus(order.VendorStatus)
if result["book"].(bool) {
order.BusinessType = model.BusinessTypeDingshida
} else {
order.BusinessType = model.BusinessTypeImmediate
}
deliveryGeo := strings.Split(utils.Interface2String(result["deliveryGeo"]), ",")
if len(deliveryGeo) == 2 {
order.CoordinateType = model.CoordinateTypeMars
order.ConsigneeLng = jxutils.StandardCoordinate2Int(utils.Str2Float64(deliveryGeo[0]))
order.ConsigneeLat = jxutils.StandardCoordinate2Int(utils.Str2Float64(deliveryGeo[1]))
}
for _, group2 := range result["groups"].([]interface{}) {
group := group2.(map[string]interface{})
for _, product2 := range group["items"].([]interface{}) {
product := product2.(map[string]interface{})
sku := &model.OrderSku{
VendorOrderID: orderID,
VendorID: model.VendorIDELM,
Count: int(utils.MustInterface2Int64(product["quantity"])),
SkuID: int(utils.Str2Int64WithDefault(utils.Interface2String(product["extendCode"]), 0)),
VendorSkuID: utils.Int64ToStr(utils.MustInterface2Int64(product["vfoodId"])),
SkuName: product["name"].(string),
SalePrice: jxutils.StandardPrice2Int(utils.MustInterface2Float64(product["price"])),
Weight: int(math.Round(utils.Interface2FloatWithDefault(product["weight"], 0.0))),
}
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, err
}
func setOrederDetailFee(result map[string]interface{}, order *model.GoodsOrder) {
orderActivities, ok := result["orderActivities"].([]interface{})
if ok {
for _, value := range orderActivities {
activity := value.(map[string]interface{})
categoryId := utils.MustInterface2Int64(activity["categoryId"])
restaurantPart := -jxutils.StandardPrice2Int(utils.MustInterface2Float64(activity["restaurantPart"]))
elemePart := -jxutils.StandardPrice2Int(utils.MustInterface2Float64(activity["elemePart"]))
if _, ok := model.ElmSkuPromotion[int(categoryId)]; ok {
order.SkuPmFee += restaurantPart
order.SkuPmSubsidy += elemePart
} else {
order.OrderPmFee += restaurantPart
order.OrderPmSubsidy += elemePart
}
}
}
order.PlatformFeeRate = int16(utils.MustInterface2Float64(result["serviceRate"]))
}
//
func (c *PurchaseHandler) onOrderNew(msg map[string]interface{}) (response *elmapi.CallbackResponse) {
// todo 这里应该可以直接用msg里的内容而不用再次去查
order, err := c.GetOrder(msg["orderId"].(string))
if err == nil {
order.VendorStatus = c.stateAndType2Str(order.VendorStatus, elmapi.MsgTypeOrderValid)
err = partner.CurOrderManager.OnOrderNew(order, c.stateAndType2Str(msg["status"].(string), elmapi.MsgTypeOrderValid))
// if globals.HandleLegacyJxOrder && err == nil {
// c.legacyWriteElmOrder(order)
// }
}
return elmapi.Err2CallbackResponse(err, "elm onOrderNew")
}
func (c *PurchaseHandler) onOrderUserUrgeOrder(msg *elmapi.CallbackOrderUrgeMsg) *elmapi.CallbackResponse {
status := &model.OrderStatus{
VendorOrderID: msg.OrderID,
VendorID: model.VendorIDELM,
OrderType: model.OrderTypeOrder,
RefVendorOrderID: msg.OrderID,
RefVendorID: model.VendorIDELM,
Status: model.OrderStatusApplyUrgeOrder,
VendorStatus: utils.Int2Str(msg.MsgType),
StatusTime: utils.Timestamp2Time(msg.UpdateTime),
}
if globals.ReallyCallPlatformAPI {
freshfood.FreshFoodAPI.ELMClientUrgeOrder(msg.OrderID)
}
return elmapi.Err2CallbackResponse(partner.CurOrderManager.OnOrderStatusChanged(status), status.VendorStatus)
}
func (c *PurchaseHandler) stateAndType2Str(state string, msgType int) string {
return fmt.Sprintf("%s-%d", state, msgType)
}
func (c *PurchaseHandler) spliltCompositeState(compositeState string) (state string, msgType int) {
index := strings.Index(compositeState, "-")
if index >= 0 {
msgType = int(utils.Str2Int64(compositeState[index+1:]))
if msgType == 0 {
globals.SugarLogger.Debug(compositeState)
}
return compositeState[:index], msgType
}
return compositeState, 0
}
// IPurchasePlatformHandler
func (c *PurchaseHandler) GetStatusFromVendorStatus(vendorStatus string) int {
state, _ := c.spliltCompositeState(vendorStatus)
if status, ok := VendorStatus2StatusMap[state]; ok {
return status
}
return model.OrderStatusUnknown
}
func (c *PurchaseHandler) AcceptOrRefuseOrder(order *model.GoodsOrder, isAcceptIt bool, userName string) (err error) {
if isAcceptIt {
err = api.ElmAPI.ConfirmOrder(order.VendorOrderID)
} else {
err = api.ElmAPI.CancelOrder(order.VendorOrderID, elmapi.CancelOrderTypeOthers, "")
}
return err
}
// 饿了么没有拣货这个状态,直接返回成功
// 真实流程中也不会调用这个方法,因为接收订单后状态会直接转移到已拣货
func (c *PurchaseHandler) PickupGoods(order *model.GoodsOrder, userName string) (err error) {
return nil
}
func (c *PurchaseHandler) Swtich2SelfDeliver(order *model.GoodsOrder, userName string) (err error) {
err = api.ElmAPI.DeliveryBySelfLite(order.VendorOrderID)
return err
}
// 饿了么转商家自送后,没有确认送达的概念,空操作
func (c *PurchaseHandler) Swtich2SelfDelivered(order *model.GoodsOrder, userName string) (err error) {
return nil
}
func (c *PurchaseHandler) SelfDeliverDelievering(order *model.GoodsOrder, userName string) (err error) {
return api.ElmAPI.StartDeliveryBySelf(order.VendorOrderID, order.ConsigneeMobile)
}
func (c *PurchaseHandler) SelfDeliverDelievered(order *model.GoodsOrder, userName string) (err error) {
return api.ElmAPI.CompleteDeliveryBySelf(order.VendorOrderID, order.ConsigneeMobile)
}
func (c *PurchaseHandler) GetStatusActionTimeout(statusType, status int) time.Duration {
if statusType == scheduler.TimerStatusTypeOrder && status == model.OrderStatusNew {
return acceptOrderDelay // 饿了么开了专送店的订单没有拣货状态,接单后就为拣货完成,所以要延迟接单,否则门店来不及备货
}
return 0
}