package tiktok_store import ( "errors" "fmt" "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/ascription_place" "regexp" "strings" "time" "git.rosy.net.cn/baseapi/platformapi/mtpsapi" order_getSettleBillDetailV3_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/order_getSettleBillDetailV3/request" order_logisticsAdd_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/order_logisticsAdd/request" order_orderDetail_response "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/order_orderDetail/response" tiktokShop "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/tiktok_api" "git.rosy.net.cn/baseapi/platformapi/mtwmapi" "git.rosy.net.cn/baseapi/utils" "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" ) const ( FakeMsgType = "fakeMsgType" fakeFinishedPickup = "fake_finished_pickup" // 假的拣货完成 fakeAcceptOrder = "fake_accept_order" fakeOrderAdjustFinished = "fake_order_adjust_finished" // 表示商户/客服/客户发起的部分退款 fakeOrderCanceled = "fake_order_canceled" // ) const ( SelfDeliveryCarrierNo = 1 // 美团配送方式:0-美团专送,1-商家自送 ) var ( specPat = regexp.MustCompile(`(\d+)(.+)`) ) var ( VendorStatus2StatusMap = map[string]int{ utils.Int2Str(tiktokShop.CreateOrderStatusWaitPay): model.OrderStatusWait4Pay, // 1待支付 utils.Int2Str(tiktokShop.CreateOrderStatusStockUp): model.OrderStatusAccepted, // 2备货中(部分发货) utils.Int2Str(tiktokShop.CreateOrderStatusDelivery): model.OrderStatusDelivering, // 3发货(发货完成) utils.Int2Str(tiktokShop.CreateOrderStatusCancel): model.OrderStatusCanceled, // 4取消 utils.Int2Str(tiktokShop.CreateOrderStatusOver): model.OrderStatusFinished, // 5完成 utils.Int2Str(tiktokShop.CreateOrderStatusBeginRefund): model.OrderStatusApplyCancel, // 21发货前退款完成 utils.Int2Str(tiktokShop.CreateOrderStatusEndRefund): model.OrderStatusAfterShipmentCancel, // 22发货后退款 utils.Int2Str(tiktokShop.CreateOrderStatusReceiptRefund): model.OrderStatusAfterReceivingCancel, // 39收货后退款 utils.Int2Str(tiktokShop.CreateOrderStatusPartialDelivery): model.OrderStatusPartDeliverGoods, // 101部分发货 utils.Int2Str(tiktokShop.CreateOrderStatusSomePay): model.OrderStatusPartPay, // 103部分支付 utils.Int2Str(tiktokShop.CreateOrderStatusAllPay): model.OrderStatusNew, // 105已支付 fakeAcceptOrder: model.OrderStatusAccepted, fakeOrderAdjustFinished: model.OrderStatusAdjust, } skuActTypeMap = map[int]int{ mtwmapi.ExtrasPromotionTypeTeJiaCai: 1, mtwmapi.ExtrasPromotionTypeZheKouCai: 1, mtwmapi.ExtrasPromotionTypeSecondHalfPrice: 1, } ) func (p *PurchaseHandler) getStatusFromVendorStatus(vendorStatus int64) int { if status, ok := VendorStatus2StatusMap[utils.Int64ToStr(vendorStatus)]; ok { return status } return model.OrderStatusUnknown } func (c *PurchaseHandler) Map2Order(orderData map[string]interface{}) (order *model.GoodsOrder) { order, _, _ = c.getOrder(orderData["VendorOrgCode"].(string), orderData["vendorOrderID"].(string), "") return order } func (p *PurchaseHandler) getOrder(vendorOrgCode, vendorOrderID, vendorStoreID string) (order *model.GoodsOrder, orderMap *order_orderDetail_response.ShopOrderDetail, err error) { // 平台获取订单详情 api := getAPI(vendorOrgCode, 0, vendorStoreID) result, err := api.GetTiktokOrderDetail(vendorOrderID) if err != nil { return nil, nil, err } orderMap = result order = &model.GoodsOrder{ VendorOrderID: result.OrderId, VendorID: model.VendorIDDD, VendorStoreID: "", StoreID: 0, CoordinateType: model.CoordinateTypeMars, BuyerComment: result.BuyerWords, PickDeadline: utils.DefaultTimeValue, VendorStatus: utils.Int64ToStr(result.OrderStatus), //1待支付/103部分支付/105已支付/2备货中/101部分发货/3已发货/4取消/完成/21发货前退款完成/22发货后退款/39收货后退款 OrderSeq: 0, StatusTime: getTimeFromTimestamp(result.CreateTime), OrderCreatedAt: getTimeFromTimestamp(result.CreateTime), OriginalData: string(utils.MustMarshal(result)), ActualPayPrice: result.PayAmount, BaseFreightMoney: result.PostAmount, InvoiceTitle: "", InvoiceTaxerID: "", InvoiceEmail: "", VendorOrgCode: vendorOrgCode, } if result.FinishTime != 0 { order.OrderFinishedAt = getTimeFromTimestamp(result.FinishTime) } else { order.OrderFinishedAt = utils.DefaultTimeValue } order.Status = p.getStatusFromVendorStatus(result.OrderStatus) if result.UserIdInfo != nil { order.VendorUserID = result.UserIdInfo.IdCardNo } originalLng := utils.MustInterface2Float64(result.UserCoordinate.UserCoordinateLongitude) originalLat := utils.MustInterface2Float64(result.UserCoordinate.UserCoordinateLatitude) order.ConsigneeLng = jxutils.StandardCoordinate2Int(originalLng) order.ConsigneeLat = jxutils.StandardCoordinate2Int(originalLat) order.DiscountMoney = result.PromotionAmount vendorStoreIdRelly := "" // 门店id vendorNameRelly := "" // 门店id var salePrice int64 = 0 // 添加需要赠送的东西(暂时没有赠品套餐直接商品) multiSkuMap := make(map[int]int) if len(result.SkuOrderList) > 0 { for _, extra := range result.SkuOrderList { sku := &model.OrderSku{ VendorOrderID: extra.ParentOrderId, VendorID: model.VendorIDDD, StoreSubID: 0, StoreSubName: "", Count: int(extra.ItemNum), VendorSkuID: utils.Int64ToStr(extra.SkuId), SkuID: utils.Str2Int(extra.OutSkuId), JxSkuID: utils.Str2Int(extra.OutSkuId), SkuName: extra.ProductName, ShopPrice: extra.OriginAmount, VendorPrice: extra.OrderAmount / extra.ItemNum, SalePrice: extra.OriginAmount, EarningPrice: 0, Weight: getSkuWeight(map[string]interface{}{"skuName": extra.ProductName}), SkuType: 0, PromotionType: 0, OrderCreatedAt: utils.Timestamp2Time(extra.CreateTime), IsVendorAct: 0, Upc: extra.Code, } activityId := make([]int64, 0) activityName := make([]string, 0) for _, v := range extra.CampaignInfo { activityId = append(activityId, v.CampaignId) activityName = append(activityName, utils.Int64ToStr(v.CampaignId)+":"+v.CampaignName) } if len(activityId) > 0 { sku.StoreSubID = activityId[0] sku.StoreSubName = strings.Join(activityName, ",") } if sku.Weight == 0 { sku.Weight = 222 // 如果名字里找不到缺省给半斤左右的一个特别值 } multiSkuMap[sku.SkuID]++ order.Skus = append(order.Skus, sku) vendorStoreIdRelly = extra.StoreInfo.StoreId vendorNameRelly = extra.StoreInfo.StoreName salePrice += sku.SalePrice } } autoTransport, err := GetStoreAutoCallRiderInfo(vendorOrgCode, []int64{utils.Str2Int64(vendorStoreIdRelly)}) if err != nil || len(autoTransport) == 0 { order.DeliveryType = model.OrderDeliveryTypePlatform } else { switch autoTransport[utils.Str2Int64(vendorStoreIdRelly)].ServiceStatus { case 1: // 1:关闭 order.DeliveryType = model.OrderDeliveryTypeStoreSelf case 2: // 开启 order.DeliveryType = model.OrderDeliveryTypePlatform } } // 预订单还是快速达 localStore, err := dao.GetStoreDetailByVendorStoreID(dao.GetDB(), vendorStoreIdRelly, model.VendorIDDD, vendorOrgCode) if err != nil || localStore == nil || localStore.ID == 0 { return nil, nil, err } if result.EarlyArrival { // 立即达 order.BusinessType = model.BusinessTypeImmediate order.ExpectedDeliveredTime = getTimeFromTimestamp(result.TargetArrivalTime + 30*60) // 预计最晚送达时间 } else { // 定时达 order.BusinessType = model.BusinessTypeDingshida order.ExpectedDeliveredTime = getTimeFromTimestamp(result.EarliestReceiptTime + 30*60) // 预计最晚送达时间 } // 用户保密信息脱敏 name, tel, address, _ := api.OrderUserInfoDecrypt(vendorOrderID, result.EncryptPostReceiver, result.EncryptPostTel, result.PostAddr.EncryptDetail) order.ConsigneeName = name order.ConsigneeMobile = tel order.ConsigneeAddress = fmt.Sprintf("%s%s%s%s%s", result.PostAddr.Province.Name, result.PostAddr.City.Name, result.PostAddr.Town.Name, result.PostAddr.Street.Name, address) order.JxStoreID = localStore.ID order.StoreID = localStore.ID order.VendorStoreID = vendorStoreIdRelly // 真实门店id order.StoreName = vendorNameRelly // 真实门店名称 order.PmSubsidyMoney = result.PlatformCostAmount // 平台承担优惠 for _, v := range order.Skus { if multiSkuMap[v.SkuID] > 1 && v.SalePrice == v.VendorPrice { v.IsVendorAct = model.YES } } // 本地获取订单记录 orderSeq, _ := dao.GetVendorOrderNumber(dao.GetDB(), model.VendorIDDD, order.VendorStoreID) order.OrderSeq = orderSeq + 1 // 抖音订单手机号和收货地址是否同城 order.PhoneAscription = "" ascription, err := ascription_place.Find(order.ConsigneeMobile) if err != nil { order.PhoneAscription = model.PhoneAscriptionAddressNo + "-" + err.Error() err = nil } else { if strings.Contains(order.ConsigneeAddress, ascription.Province) && strings.Contains(order.ConsigneeAddress, ascription.City) { order.PhoneAscription = model.PhoneAscriptionAddressYes + "-" + ascription.Province + ascription.City } else { order.PhoneAscription = model.PhoneAscriptionAddressNo + "-" + "归属信息不匹配:" + ascription.Province + ascription.City } } store, _ := dao.GetStoreDetail(dao.GetDB(), order.JxStoreID, order.VendorID, order.VendorOrgCode) order.PackagePrice = store.PackageSetting jxutils.RefreshOrderSkuRelated(order) return order, orderMap, nil } // GetOrderRider 商家自配送同步配送信息 func (p *PurchaseHandler) GetOrderRider(vendorOrgCode, vendorStoreID string, param map[string]interface{}) (err error) { appKey := `7152420904331429407` // 暂时定死 appSecret := `cc7ba367-2394-4cbb-81c6-26f0e929d1c6` //暂时定死 return tiktokShop.NewExpress(appKey, appSecret, "").OrderStatusAndPsInfo(param) } func (p *PurchaseHandler) GetOrder(vendorOrgCode, vendorOrderID, vendorStoreID string) (order *model.GoodsOrder, err error) { order, _, err = p.getOrder(vendorOrgCode, vendorOrderID, vendorStoreID) return order, err } func (p *PurchaseHandler) getOrderMap(vendorOrgCode, vendorOrderID, vendorStoreID string) (orderMap *order_orderDetail_response.ShopOrderDetail, err error) { _, orderMap, err = p.getOrder(vendorOrgCode, vendorOrderID, vendorStoreID) return orderMap, err } // 获取部分商家退款的物品(缺货缺重) func getRefundSkuDetailList(order *model.GoodsOrder) (skuList []order_orderDetail_response.SkuOrderListItem, err error) { refundOrderDetailList, err := getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").GetTiktokOrderDetail(order.VendorOrderID) if err != nil { return nil, err } for _, v := range refundOrderDetailList.SkuOrderList { if v.OrderStatus == tiktokShop.CreateOrderStatusCancel { skuList = append(skuList, v) } } return skuList, err } func getSkuWeight(product map[string]interface{}) (weight int) { _, _, _, specUnit, _, specQuality := jxutils.SplitSkuName(product["skuName"].(string)) weight = jxutils.FormatSkuWeight(specQuality, specUnit) return weight } func (c *PurchaseHandler) onOrderMsg(msgId, orderId string, msg interface{}) (response *tiktokShop.CallbackResponse) { var err error // 处理售后单 if c.isAfsMsg(msgId) { response = c.OnAfsOrderMsg(msgId, orderId, msg) return response } //抖音运单处理 if msgId == tiktokShop.CallbackShipmentInfoChange { //msgId-骑手运单号 orderId-抖音店铺ID msg-回调运单参数 return c.onWaybillMsg(msgId, orderId, msg) } // 待支付订单将不做处理/支付订单待处理(抖音风控) if msgId == tiktokShop.CallbackCreatedOrderMsgTagId || msgId == tiktokShop.CallbackWaitOrderMsgTagId { return tiktokShop.Err2CallbackResponse(nil, "") } // 组装订单状态变化 status, appOrgCode := c.callbackMsg2Status(msgId, orderId, msg) // 校验重复消息 if partner.CurOrderManager.GetStatusDuplicatedCount(status) > 0 { return tiktokShop.Err2CallbackResponse(nil, "") } // 已经支付的订单,当做新订单创建在平台内部 ----------------- if msgId == tiktokShop.CallbackPayOrderMsgTagId || msgId == tiktokShop.CallbackChangeMoneyMsgTagId { order, err := c.GetOrder(utils.Int64ToStr(appOrgCode), orderId, "") // 获取平台订单详情,制作本地订单 if err != nil { return tiktokShop.Err2CallbackResponse(err, "") } if err := partner.CurOrderManager.OnOrderNew(order, status); err != nil { return tiktokShop.Err2CallbackResponse(err, "") } orderMap, err := c.getOrderMap(utils.Int64ToStr(appOrgCode), orderId, "") utils.CallFuncAsync(func() { switch msgId { case tiktokShop.CallbackPayOrderMsgTagId: c.OnOrderDetail(orderMap, partner.CreatedPeration) case tiktokShop.CallbackChangeMoneyMsgTagId: c.OnOrderDetail(orderMap, partner.UpdatedPeration) } }) } else { if status == nil { return tiktokShop.Err2CallbackResponse(errors.New("order_status create err"), "") } var order *model.GoodsOrder if order, err = partner.CurOrderManager.LoadOrder(orderId, model.VendorIDDD); err != nil { return tiktokShop.Err2CallbackResponse(err, "") } if status.Status == model.OrderStatusAdjust { // 部分退款商品调整(缺货) skuList, err2 := getRefundSkuDetailList(order) if err = err2; err == nil { var removedSkuList []*model.OrderSku for _, ttSku := range skuList { order.ActualPayPrice -= ttSku.PayAmount removedSkuList = append(removedSkuList, &model.OrderSku{ SkuID: int(utils.Str2Int64WithDefault(ttSku.OutSkuId, 0)), Count: int(ttSku.ItemNum), }) } order = jxutils.RemoveSkuFromOrder(order, removedSkuList) jxutils.RefreshOrderSkuRelated(order) err = partner.CurOrderManager.OnOrderAdjust(order, status) } } else { // 发货完成 if msgId == tiktokShop.CallbackPartAllGoodsMsgTagId || msgId == tiktokShop.CallbackRefundOrderMsgTagId { // || msgId == tiktokShop.CallbackPartGoodsMsgTagId 部分发货 utils.CallFuncAsync(func() { orderMap, _ := getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").GetTiktokOrderDetail(orderId) c.OnOrderDetail(orderMap, partner.UpdatedPeration) }) } if err := partner.CurOrderManager.OnOrderStatusChanged(order.VendorOrgCode, status); err != nil { return tiktokShop.Err2CallbackResponse(err, "") } } } return tiktokShop.Err2CallbackResponse(err, "") } func (c *PurchaseHandler) callbackMsg2Status(msgId, orderId string, msg interface{}) (*model.OrderStatus, int64) { orderStatus := &model.OrderStatus{} var shopId int64 = 0 switch msgId { case tiktokShop.CallbackCreatedOrderMsgTagId: return nil, 0 // 抖音新创建订单,未支付时本地不做创建,可能取消 //orderMsg := msg.(*tiktokShop.CreateOrderData) //orderStatus.VendorOrderID = utils.Int64ToStr(orderMsg.PId) //orderStatus.VendorStatus = utils.Int64ToStr(orderMsg.OrderStatus) //orderStatus.StatusTime = utils.Timestamp2Time(orderMsg.CreateTime) //orderStatus.Status = vendorStatusToLocalStatus(orderMsg.OrderStatus) //orderStatus.Remark = "100-新订单回调" case tiktokShop.CallbackPayOrderMsgTagId: orderMsg := tiktokShop.PayOrderData{} if err := utils.Map2StructByJson(msg, &orderMsg, false); err != nil { return nil, 0 } orderStatus.VendorOrderID = utils.Int64ToStr(orderMsg.PId) orderStatus.VendorStatus = utils.Int64ToStr(orderMsg.OrderStatus) orderStatus.StatusTime = utils.Timestamp2Time(orderMsg.PayTime) orderStatus.Status = vendorStatusToLocalStatus(orderMsg.OrderStatus) orderStatus.Remark = "101-支付回调" shopId = orderMsg.ShopId //case tiktokShop.CallbackWaitOrderMsgTagId: // return nil, 0 // 支付订单风控消息不处理 //orderMsg := msg.(*tiktokShop.WaitOrderData) //orderStatus.VendorOrderID = utils.Int64ToStr(orderMsg.PId) //orderStatus.VendorStatus = utils.Int64ToStr(orderMsg.OrderStatus) //orderStatus.StatusTime = utils.Timestamp2Time(orderMsg.PayTime) //orderStatus.Status = vendorStatusToLocalStatus(orderMsg.OrderStatus) //orderStatus.Remark = "110-订单支付待处理:风控" case tiktokShop.CallbackPartGoodsMsgTagId: orderMsg := tiktokShop.SomeSendOrderData{} if err := utils.Map2StructByJson(msg, &orderMsg, false); err != nil { return nil, 0 } orderStatus.VendorOrderID = utils.Int64ToStr(orderMsg.PId) orderStatus.VendorStatus = utils.Int64ToStr(orderMsg.OrderStatus) // 2 orderStatus.StatusTime = utils.Timestamp2Time(orderMsg.UpdateTime) orderStatus.Status = model.OrderStatusAdjust // vendorStatusToLocalStatus(orderMsg.OrderStatus) orderStatus.Remark = "108-卖家部分发货" shopId = orderMsg.ShopId case tiktokShop.CallbackPartAllGoodsMsgTagId: orderMsg := tiktokShop.BusinessDeliveryData{} if err := utils.Map2StructByJson(msg, &orderMsg, false); err != nil { return nil, 0 } orderStatus.VendorOrderID = utils.Int64ToStr(orderMsg.PId) orderStatus.VendorStatus = utils.Int64ToStr(orderMsg.OrderStatus) // 3 orderStatus.StatusTime = utils.Timestamp2Time(orderMsg.UpdateTime) orderStatus.Status = model.OrderStatusAdjust // vendorStatusToLocalStatus(orderMsg.OrderStatus) orderStatus.Remark = "102-卖家发货完成" shopId = orderMsg.ShopId case tiktokShop.CallbackCancelOrderMsgTagId: orderMsg := tiktokShop.CancelOrderData{} if err := utils.Map2StructByJson(msg, &orderMsg, false); err != nil { return nil, 0 } orderStatus.VendorOrderID = utils.Int64ToStr(orderMsg.PId) orderStatus.VendorStatus = utils.Int64ToStr(orderMsg.OrderStatus) orderStatus.StatusTime = utils.Timestamp2Time(orderMsg.CancelTime) orderStatus.Status = vendorStatusToLocalStatus(orderMsg.OrderStatus) orderStatus.Remark = "106-取消订单" shopId = orderMsg.ShopId case tiktokShop.CallbackSuccessOrderMsgTagId: // 交易完成 orderMsg := tiktokShop.SuccessOrderData{} if err := utils.Map2StructByJson(msg, &orderMsg, false); err != nil { return nil, 0 } orderStatus.VendorOrderID = utils.Int64ToStr(orderMsg.PId) orderStatus.VendorStatus = utils.Int64ToStr(orderMsg.OrderStatus) orderStatus.StatusTime = utils.Timestamp2Time(orderMsg.CompleteTime) orderStatus.Status = vendorStatusToLocalStatus(orderMsg.OrderStatus) orderStatus.Remark = "103-确认收货/交易完成" shopId = orderMsg.ShopId case tiktokShop.CallbackWayBillChangeOrderMsgTagId: orderMsg := tiktokShop.WayBillChangeData{} if err := utils.Map2StructByJson(msg, &orderMsg, false); err != nil { return nil, 0 } orderStatus.VendorOrderID = utils.Int64ToStr(orderMsg.PId) orderStatus.VendorStatus = utils.Int64ToStr(orderMsg.OrderStatus) orderStatus.StatusTime = utils.Timestamp2Time(orderMsg.UpdateTime) orderStatus.Status = vendorStatusToLocalStatus(orderMsg.OrderStatus) orderStatus.Remark = "104-物流消息变更" shopId = orderMsg.ShopId case tiktokShop.CallbackReceivingChangeOrderMsgTagId: orderMsg := tiktokShop.ReceivingAddressChangeData{} if err := utils.Map2StructByJson(msg, &orderMsg, false); err != nil { return nil, 0 } orderStatus.VendorOrderID = utils.Int64ToStr(orderMsg.PId) orderStatus.VendorStatus = utils.Int64ToStr(orderMsg.OrderStatus) orderStatus.StatusTime = utils.Timestamp2Time(orderMsg.UpdateTime) orderStatus.Status = vendorStatusToLocalStatus(orderMsg.OrderStatus) orderStatus.Remark = "105-收货地址变更" shopId = orderMsg.ShopId case tiktokShop.CallbackChangeMoneyMsgTagId: orderMsg := tiktokShop.UpdateAmountChangeData{} if err := utils.Map2StructByJson(msg, &orderMsg, false); err != nil { return nil, 0 } orderStatus.VendorOrderID = utils.Int64ToStr(orderMsg.PId) orderStatus.VendorStatus = utils.Int64ToStr(orderMsg.OrderStatus) orderStatus.StatusTime = utils.Timestamp2Time(orderMsg.ModifyTime) orderStatus.Status = vendorStatusToLocalStatus(orderMsg.OrderStatus) orderStatus.Remark = "109-卖家修改订单/运单金额" shopId = orderMsg.ShopId case tiktokShop.CallbackApplyUpdateAddressMsgTagId: orderMsg := tiktokShop.BuyerUpdateWayBillData{} if err := utils.Map2StructByJson(msg, &orderMsg, false); err != nil { return nil, 0 } orderStatus.VendorOrderID = utils.Int64ToStr(orderMsg.PId) orderStatus.VendorStatus = utils.Int64ToStr(tiktokShop.CreateOrderStatusStockUp) orderStatus.StatusTime = time.Now() orderStatus.Status = model.OrderStatusUnknown orderStatus.Remark = "111-买家收货消息变更" shopId = orderMsg.ShopId case tiktokShop.CallbackBusinessRemarkMsgTagId: orderMsg := tiktokShop.BusinessUpdateRemakeData{} if err := utils.Map2StructByJson(msg, &orderMsg, false); err != nil { return nil, 0 } orderStatus.VendorOrderID = utils.Int64ToStr(orderMsg.PId) orderStatus.VendorStatus = utils.Int64ToStr(tiktokShop.CreateOrderStatusStockUp) orderStatus.StatusTime = utils.Timestamp2Time(orderMsg.UpdateTime) orderStatus.Status = model.OrderStatusUnknown orderStatus.Remark = "113-添加备注:" + orderMsg.Remark shopId = orderMsg.ShopId case tiktokShop.CallbackSendOrderTimeChangeMsgTagId: orderMsg := tiktokShop.AppointmentChangeData{} if err := utils.Map2StructByJson(msg, &orderMsg, false); err != nil { return nil, 0 } orderStatus.VendorOrderID = orderMsg.PId orderStatus.VendorStatus = utils.Int64ToStr(tiktokShop.CreateOrderStatusStockUp) orderStatus.StatusTime = time.Now() orderStatus.Status = model.OrderStatusUnknown orderStatus.Remark = "126-预约发货:" shopId = orderMsg.ShopId case FakeMsgType: // 应为需要同步京东,美团拣货功能制作的假拣货,抖音不存在拣货 orderMsg := tiktokShop.FakePickUpGoodsData{} if err := utils.Map2StructByJson(msg, &orderMsg, false); err != nil { return nil, 0 } orderStatus.VendorOrderID = orderMsg.PId orderStatus.VendorStatus = fakeFinishedPickup orderStatus.StatusTime = time.Now() orderStatus.Status = model.OrderStatusFinishedPickup orderStatus.Remark = "15-抖音假拣货:" shopId = orderMsg.ShopId default: return nil, 0 } orderStatus.OrderType = model.OrderTypeOrder orderStatus.RefVendorOrderID = orderStatus.VendorOrderID orderStatus.VendorID = model.VendorIDDD orderStatus.RefVendorID = model.VendorIDDD return orderStatus, shopId } // 将抖音平台订单状态修改为本地通用状态(不好用) func vendorStatusToLocalStatus(vendorStatus int64) int { if status, ok := VendorStatus2StatusMap[utils.Int64ToStr(vendorStatus)]; ok { return status } return model.OrderStatusUnknown } func (c *PurchaseHandler) postFakeMsg(vendorOrderID, cmd, VendorStatus, appOrgCode string) { // c.postFakeMsg(order.VendorOrderID, FakeMsgType, fakeFinishedPickup) msg := &tiktokShop.FakePickUpGoodsData{ PId: vendorOrderID, VendorStatus: VendorStatus, Cmd: cmd, ShopId: utils.Str2Int64(appOrgCode), } utils.CallFuncAsync(func() { c.onOrderMsg(cmd, vendorOrderID, msg) }) } // AcceptOrRefuseOrder 接单或者拒单(isAcceptIt:接单/拒单) 抖店暂无拒单 func (c *PurchaseHandler) AcceptOrRefuseOrder(order *model.GoodsOrder, isAcceptIt bool, userName string) (err error) { return err } // PickupGoods 拣货完成 (抖音无拣货接口) func (c *PurchaseHandler) PickupGoods(order *model.GoodsOrder, isSelfDelivery bool, userName string) (err error) { // 通知平台拣货完成,抖店只需要本地拣货完成 // 抖音需要订单在门店的营业时间范围内最多超过营业时间内才自动拣货 c.postFakeMsg(order.VendorOrderID, FakeMsgType, fakeFinishedPickup, order.VendorOrgCode) return nil } 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 } // CanSwitch2SelfDeliver 美团预定单不能转商家自送 func (c *PurchaseHandler) CanSwitch2SelfDeliver(order *model.GoodsOrder) (isCan bool, err error) { return order.BusinessType != model.BusinessTypeDingshida, nil } // Swtich2SelfDeliver 抖音转自送的时候就是通知抖音发货了 func (c *PurchaseHandler) Swtich2SelfDeliver(order *model.GoodsOrder, userName string) (err error) { api := getAPI(order.VendorOrgCode, 0, "") remoteWaybill, err2 := c.GetDYPSRiderInfo(order.VendorOrderID) if err2 != nil { return err2 } if remoteWaybill.ShipmentStatus >= tiktokShop.ShipmentStatusArrived { //骑手取货后不可取消 return errors.New("抖音配送骑手已取货,不可转门店自配送/三方配送") } if len(remoteWaybill.TrackNo) != 0 { //存在运单 if err := api.ShopOrderDispatcher(utils.Str2Int64(order.VendorStoreID), order.VendorOrderID, tiktokShop.DispatcherFeeTypeCancel); err != nil { return err } } //转门店自送通知抖音发货 err = api.OrderDelivering(&order_logisticsAdd_request.OrderLogisticsAddParam{ OrderId: order.VendorOrderID, Company: "", CompanyCode: "chengdouruoxi", LogisticsCode: order.VendorOrderID, IsRefundReject: false, IsRejectRefund: false, SerialNumberList: nil, AddressId: 0, StoreId: utils.Str2Int64(order.VendorStoreID), }) return err } // Swtich2SelfDelivered 暂无自送完成 func (c *PurchaseHandler) Swtich2SelfDelivered(order *model.GoodsOrder, userName string) (err error) { if globals.EnableMtwmStoreWrite { // 您好,之前的答复已经更正为,调用变更配送状态的接口,会校验门店的配送类型。美团配送的门店即便转自配后因门店配送类型是美团配送所以无法调用接口变更配送状态。可提醒顾客点击确认收货。谢谢 // 非自配送门店订单调用OrderArrived好像会报错:{"data":"ng","error":{"code":1038,"msg":"只允许商家配送调用该接口"}} // err = api.MtwmAPI.OrderArrived(utils.Str2Int64(order.VendorOrderID)) } return err } // SelfDeliverDelivering 订单在自配送中,表示三方品牌接单,通知抖音已经发货了 func (c *PurchaseHandler) SelfDeliverDelivering(order *model.GoodsOrder, userName string) (err error) { param := &order_logisticsAdd_request.OrderLogisticsAddParam{ OrderId: order.VendorOrderID, Company: "", CompanyCode: "chengdouruoxi", LogisticsCode: order.VendorOrderID, IsRefundReject: false, IsRejectRefund: false, SerialNumberList: nil, AddressId: 0, StoreId: utils.Str2Int64(order.VendorStoreID), } err = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").OrderDelivering(param) if err != nil { return err } riderInfo := &mtpsapi.RiderInfo{ OrderId: order.VendorOrderID, ThirdCarrierOrderId: order.VendorOrderID, CourierName: "", CourierPhone: "", LogisticsProviderCode: "10002", LogisticsStatus: 0, LogisticsContext: "呼叫骑手,新建运单", Latitude: "", Longitude: "", OpCode: tiktokShop.TiktokLogisticsStatusCALLRIDER, } return c.GetOrderRider("", "", utils.Struct2MapByJson(riderInfo)) } // SelfDeliverDelivered 自配搜完成(暂无) func (c *PurchaseHandler) SelfDeliverDelivered(order *model.GoodsOrder, userName string) (err error) { riderInfo := &mtpsapi.RiderInfo{ OrderId: order.VendorOrderID, ThirdCarrierOrderId: order.VendorOrderID, CourierName: "", CourierPhone: "", LogisticsProviderCode: "10002", LogisticsStatus: 0, LogisticsContext: "商家自配送,已送达", Latitude: "", Longitude: "", OpCode: tiktokShop.TiktokLogisticsDELIVERED, } return c.GetOrderRider("", "", utils.Struct2MapByJson(riderInfo)) } func getTimeFromTimestamp(timeStamp int64) time.Time { if timeStamp < 1538103149 { // 立即达订单给的是1(而不是空,0),1538103149不是特殊值,只是一个任意之前的时间,这样写可以处理 return utils.DefaultTimeValue } return utils.Timestamp2Time(timeStamp) } // GetOrderRealMobile 获取真实号 func (c *PurchaseHandler) GetOrderRealMobile(ctx *jxcontext.Context, order *model.GoodsOrder) (mobile string, err error) { return "", err } // AgreeOrRefuseCancel 同意或拒绝(退款) func (c *PurchaseHandler) AgreeOrRefuseCancel(ctx *jxcontext.Context, order *model.GoodsOrder, isAgree bool, reason string) (err error) { afsOrder, err := partner.CurOrderManager.LoadAfsOrder(order.VendorOrderID, order.VendorID) if err != nil { return err } api := getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "") if globals.EnableDdStoreWrite { if isAgree { errList := make([]string, 0, 0) // 同意仅退款 if err = api.AfterSaleOperate(tiktokShop.AfterSaleEmuAgreeOnlyRefundApply, afsOrder.AfsOrderID, reason, utils.Str2Int64(order.VendorStoreID)); err != nil { errList = append(errList, err.Error()) } else { return nil } if err = api.AfterSaleOperate(tiktokShop.AfterSaleEmuAgreeToReturnOneApply, afsOrder.AfsOrderID, reason, utils.Str2Int64(order.VendorStoreID)); err != nil { errList = append(errList, err.Error()) } else { return nil } if err = api.AfterSaleOperate(tiktokShop.AfterSaleEmuAgreeToReturnTwoApply, afsOrder.AfsOrderID, reason, utils.Str2Int64(order.VendorStoreID)); err != nil { errList = append(errList, err.Error()) } else { return nil } if len(errList) < 3 { return nil } return fmt.Errorf("%s", strings.Join(errList, "--")) } else { errList := make([]string, 0, 0) // 拒绝退款 if err = api.AfterSaleOperate(tiktokShop.AfterSaleEmuRefuseOnlyRefundApply, afsOrder.AfsOrderID, reason, utils.Str2Int64(order.VendorStoreID)); err != nil { errList = append(errList, err.Error()) } else { return nil } if err = api.AfterSaleOperate(tiktokShop.AfterSaleEmuRefuseToReturnOneApply, afsOrder.AfsOrderID, reason, utils.Str2Int64(order.VendorStoreID)); err != nil { errList = append(errList, err.Error()) } else { return nil } if err = api.AfterSaleOperate(tiktokShop.AfterSaleEmuRefuseToReturnTwoApply, afsOrder.AfsOrderID, reason, utils.Str2Int64(order.VendorStoreID)); err != nil { errList = append(errList, err.Error()) } else { return nil } if len(errList) < 3 { return nil } return fmt.Errorf("%s", strings.Join(errList, "--")) } } return err } // CancelOrder 取消订单 (抖音商家无法发起退单操作,商家联系不到客户取消订单/拒收/缺货) func (c *PurchaseHandler) CancelOrder(ctx *jxcontext.Context, order *model.GoodsOrder, reason string) (err error) { // 售后原因枚举 MissGram = 1缺重,必传克数、金额; MissItem = 2缺货,只支持整单退; RefuseSign = 3 拒收,支持按件数退,金额由抖音计算 // 获取订单商品数量 var count = 0 for _, v := range order.Skus { count += v.Count } api := getAPI(order.VendorOrgCode, 0, "") // 获取订单详情 orderDetail, err := api.GetTiktokOrderDetail(order.VendorOrderID) if err != nil { return err } for _, v := range orderDetail.SkuOrderList { _, err = api.ApplyMarketAfterSale(utils.Str2Int64(v.OrderId), v.ItemNum, 3) } // 调用开放平台接口取消订单,不推送取消订单消息和退款消息。 c.postFakeMsg(order.VendorOrderID, tiktokShop.CallbackCancelOrderMsgTagId, utils.Int2Str(tiktokShop.CreateOrderStatusCancel), order.VendorOrgCode) if err != nil { return errors.New(err.Error() + "-" + "系统退单错误请多次退单/联系管理员/前往抖店平台退单") } return err } // AdjustOrder 发起部分退款 func (c *PurchaseHandler) AdjustOrder(ctx *jxcontext.Context, order *model.GoodsOrder, removedSkuList []*model.OrderSku, reason string) (err error) { // 拣货完成之后,在部分退款(抖店为缺货/缺重) if order.Status < model.OrderStatusFinishedPickup { if err = c.PickupGoods(order, false, ctx.GetUserName()); err != nil { return err } } for _, sku := range removedSkuList { // todo 商家发起部分退单,传入参数为子订单id,子订单id需要存储,返回售后单id _, err = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").ApplyMarketAfterSale(utils.Str2Int64(sku.VendorOrderID), int64(sku.Count), 2) } 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必须指定") } queryDate = utils.Time2Date(queryDate) var vendorStoreIDs []string if vendorStoreID == "" { return nil, fmt.Errorf("门店id需要填写") //vendorStoreIDs, err = c.GetAllStoresVendorID(ctx, vendorOrgCode) //if err != nil { // return nil, err //} } else { vendorStoreIDs = []string{vendorStoreID} } task := tasksch.NewParallelTask("tiktok shop ListOrders", nil, ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { vendorStoreID := batchItemList[0].(string) var orderIDs []string // 获取当前门店当天的订单 if vendorOderID, err2 := getAPI(vendorOrgCode, 0, vendorStoreID).GetStoreOrderList(queryDate, utils.Str2Int64(vendorStoreID)); err2 == nil { orderIDs = vendorOderID } retVal = orderIDs return retVal, nil }, 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 { vendorOrderIDs[k] = v.(string) } } return vendorOrderIDs, err } // func (c *PurchaseHandler) UpdateWaybillTip(ctx *jxcontext.Context, order *model.GoodsOrder, tipFee int64) (err error) { // if globals.EnableMtwmStoreWrite { // err = api.MtwmAPI.OrderUpdateTip(utils.Str2Int64(order.VendorOrderID), jxutils.IntPrice2Standard(tipFee)) // } // return err // } // GetOrderConsigneeNumber 获取用户真实订单号 func (p *PurchaseHandler) GetOrderConsigneeNumber(ctx *jxcontext.Context, storeID int, vendorStoreID string) (numberList []*partner.OrderPhoneNumberInfo, err error) { //offset := 0 //for { // store, _ := dao.GetStoreDetail(dao.GetDB(), storeID, model.VendorIDMTWM, "") // result, err2 := getAPI(store.VendorOrgCode, storeID, "").OrderBatchPullPhoneNumber(vendorStoreID, offset, mtwmapi.MaxBatchPullPhoneNumberLimit) // if err = err2; err == nil { // for _, v := range result { // v2 := &partner.OrderPhoneNumberInfo{ // VendorOrderID: utils.Int64ToStr(v.OrderID), // PhoneNumber: v.RealPhoneNumber, // } // if v2.PhoneNumber == "" { // v2.PhoneNumber = v.RealOrderPhoneNumber // } // numberList = append(numberList, v2) // } // if len(result) <= mtwmapi.MaxBatchPullPhoneNumberLimit { // break // } // offset += mtwmapi.MaxBatchPullPhoneNumberLimit // } else { // break // } //} //return numberList, err return nil, nil } // GetOrderCourierNumber 获取配送员电话号码 func (p *PurchaseHandler) GetOrderCourierNumber(ctx *jxcontext.Context, storeID int, vendorStoreID string) (numberList []*partner.OrderPhoneNumberInfo, err error) { //offset := 0 //for { // store, _ := dao.GetStoreDetail(dao.GetDB(), storeID, model.VendorIDMTWM, "") // result, err2 := getAPI(store.VendorOrgCode, 0, "").OrderGetRiderInfoPhoneNumber(vendorStoreID, offset, mtwmapi.MaxBatchPullPhoneNumberLimit) // if err = err2; err == nil { // for _, v := range result { // numberList = append(numberList, &partner.OrderPhoneNumberInfo{ // VendorOrderID: utils.Int64ToStr(v.OrderID), // PhoneNumber: v.RiderRealPhoneNumber, // }) // } // if len(result) <= mtwmapi.MaxBatchPullPhoneNumberLimit { // break // } // offset += mtwmapi.MaxBatchPullPhoneNumberLimit // } else { // break // } //} return numberList, err } func (p *PurchaseHandler) onNumberDowngrade(msg *mtwmapi.CallbackMsg) (response *mtwmapi.CallbackResponse) { //userNumberMap := make(map[string]*partner.OrderPhoneNumberInfo) //courierNumberMap := make(map[string]*partner.OrderPhoneNumberInfo) //orderMap := make(map[string]int) //ctx := jxcontext.AdminCtx //task := tasksch.NewParallelTask("美团外卖平台处理隐私号降级通知", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx, // func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { // step := batchItemList[0].(int) // switch step { // case 0: // userNumberList, err2 := p.GetOrderConsigneeNumber(ctx, 0, "") // if err = err2; err == nil { // for _, v := range userNumberList { // userNumberMap[v.VendorOrderID] = v // orderMap[v.VendorOrderID] = 1 // } // } // case 1: // courierNumberList, err2 := p.GetOrderCourierNumber(ctx, 0, "") // if err = err2; err == nil { // for _, v := range courierNumberList { // courierNumberMap[v.VendorOrderID] = v // orderMap[v.VendorOrderID] = 1 // } // } // case 2: // orderList := jxutils.StringMap2List(orderMap) // if len(orderList) > 0 { // updateTask := tasksch.NewParallelTask("美团外卖平台处理隐私号降级通知/处理订单", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx, // func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { // vendorOrderID := batchItemList[0].(string) // db := dao.GetDB() // if userNumberMap[vendorOrderID] != nil { // _, err = dao.UpdateEntityByKV(db, &model.GoodsOrder{}, map[string]interface{}{ // "ConsigneeMobile": userNumberMap[vendorOrderID].PhoneNumber, // "ConsigneeMobile2": userNumberMap[vendorOrderID].PhoneNumber, // }, map[string]interface{}{ // model.FieldVendorOrderID: vendorOrderID, // model.FieldVendorID: model.VendorIDMTWM, // }) // } // if courierNumberMap[vendorOrderID] != nil { // _, err = dao.UpdateEntityByKV(db, &model.Waybill{}, map[string]interface{}{ // "CourierMobile": courierNumberMap[vendorOrderID].PhoneNumber, // }, map[string]interface{}{ // "VendorWaybillID": vendorOrderID, // "WaybillVendorID": model.VendorIDMTWM, // }) // } // return retVal, err // }, orderList) // tasksch.HandleTask(updateTask, task, true).Run() // _, err = updateTask.GetResult(0) // } // } // return retVal, err // }, []int{0, 1, 2}) //tasksch.HandleTask(task, nil, true).Run() return response } // GetWaybillTip 获取订单小费 func (c *PurchaseHandler) GetWaybillTip(ctx *jxcontext.Context, vendorOrgCode, vendorStoreID, vendorOrderID, vendorWaybillID, vendorWaybillID2 string) (tipFee int64, err error) { //orderInfo, err := getAPI(vendorOrgCode, 0, vendorStoreID).GetDistributeOrderDetail(vendorOrderID, vendorStoreID) //if err == nil { // tipFee = jxutils.StandardPrice2Int(orderInfo.TipAmount) //} //return tipFee, err return 0, nil } // UpdateWaybillTip 修改订单小费 func (c *PurchaseHandler) UpdateWaybillTip(ctx *jxcontext.Context, vendorOrgCode, vendorStoreID, vendorOrderID, vendorWaybillID, vendorWaybillID2, cityCode string, tipFee int64) (err error) { //if globals.EnableMtwmStoreWrite { // err = getAPI(vendorOrgCode, 0, vendorStoreID).OrderModityTips(vendorOrderID, vendorStoreID, jxutils.IntPrice2Standard(tipFee)) //} return err } func (c *PurchaseHandler) GetSelfTakeCode(ctx *jxcontext.Context, order *model.GoodsOrder) (selfTakeCode string, err error) { return selfTakeCode, err } func (c *PurchaseHandler) ConfirmSelfTake(ctx *jxcontext.Context, order *model.GoodsOrder, selfTakeCode string) (err error) { return err } func (c *PurchaseHandler) ComplaintRider(vendorOrderId string, resonID int, resonContent string) (err error) { return err } // GetCancelDeliveryReason 转自配送时取消非专送混合送门店取消理由 func (c *PurchaseHandler) GetCancelDeliveryReason(order *model.GoodsOrder) (string, error) { //reason, err := getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").GetCancelDeliveryReason(utils.Str2Int64(order.VendorOrderID), order.VendorStoreID) //if err != nil { // return "", err //} //return reason, nil return "", nil } // CancelLogisticsByWmOrderId 取消平台配送,使用现有的三方配送 func (c *PurchaseHandler) CancelLogisticsByWmOrderId(order *model.GoodsOrder, reasonCode, detailContent, appPoiCode, orderId string) error { return getAPI(order.VendorOrgCode, 0, "").ShopOrderDispatcher(utils.Str2Int64(order.VendorStoreID), order.VendorOrderID, tiktokShop.DispatcherFeeTypeCancel) } // OrderLogisticsStatus 获取订单配送状态 func (c *PurchaseHandler) OrderLogisticsStatus(orderId int64) (int64, error) { orderInfo, err := dao.GetSimpleOrder(dao.GetDB(), utils.Int64ToStr(orderId)) if err != nil { return 0, errors.New("获取本地门店账号信息失败,请重试") } waybill, err := getAPI(orderInfo.VendorOrgCode, 0, "").GetShipmentInfo(utils.Str2Int64(orderInfo.VendorOrderID), 0, tiktokShop.ShipmentTypeInvoice) if err != nil { return 0, err } return waybill.ShipmentStatus, nil } // GetOrderTotalShopMoney 获取门店结算信息 func GetOrderTotalShopMoney(appOrgCode string, orderIds string, nextStartIndex string) (int64, string, error) { return getAPI(appOrgCode, 0, "").GetSettleBillDetailV3(&order_getSettleBillDetailV3_request.OrderGetSettleBillDetailV3Param{ Size: 50, OrderId: orderIds, StartIndex: nextStartIndex, }) } // GetOrderDetail 获取订单详情 func GetOrderDetail(appOrgCode, vendorOrderID string) (*order_orderDetail_response.ShopOrderDetail, error) { return getAPI(appOrgCode, 0, "").GetTiktokOrderDetail(vendorOrderID) } // GetOrderSettleAccounts 获取订单结算信息 func (c *PurchaseHandler) GetOrderSettleAccounts(order *model.GoodsOrder) (int64, error) { return 0, nil }