package fn import ( "fmt" tao "git.rosy.net.cn/baseapi/platformapi/tao_vegetable" "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/business/jxcallback/orderman" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/business/partner/purchase/tao_vegetable" "strings" "time" "git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/partner/delivery" "git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/baseapi/platformapi/fnpsapi" "git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/partner" "git.rosy.net.cn/jx-callback/globals/api" ) const ( ComplaintReasonsFn150 = 150 //:未保持餐品完整, ComplaintReasonsFn220 = 220 //:少餐错餐, ComplaintReasonsFn160 = 160 //:服务态度恶劣, ComplaintReasonsFn190 = 190 //:额外索取费用, ComplaintReasonsFn170 = 170 //:诱导收货人或商户退单, ComplaintReasonsFn140 = 140 //:提前点击送达, ComplaintReasonsFn210 = 210 //:虚假标记异常, ComplaintReasonsFn200 = 200 //:虚假点击配送成功, ComplaintReasonsFn130 = 130 //:未进行配送,导致订单失败或取消, ComplaintReasonsFn120 = 120 //:配送超时 ) var ( CurDeliveryHandler *DeliveryHandler complaintReson2FnResonMap = map[int]int{ model.ComplaintReasons1: ComplaintReasonsFn160, model.ComplaintReasons2: ComplaintReasonsFn130, model.ComplaintReasons3: ComplaintReasonsFn130, model.ComplaintReasons4: ComplaintReasonsFn120, model.ComplaintReasons5: ComplaintReasonsFn130, model.ComplaintReasons6: ComplaintReasonsFn150, model.ComplaintReasons7: ComplaintReasonsFn190, model.ComplaintReasons69: ComplaintReasonsFn170, model.ComplaintReasons71: ComplaintReasonsFn140, } ) type DeliveryHandler struct { } func init() { CurDeliveryHandler = new(DeliveryHandler) partner.RegisterDeliveryPlatform(CurDeliveryHandler, true) } func (c *DeliveryHandler) GetVendorID() int { return model.VendorIDFengNiao } func (c *DeliveryHandler) CancelWaybill(bill *model.Waybill, cancelReasonID int, cancelReason string) (err error) { parameter := &fnpsapi.CancelOrderReq{ OrderCancelCode: fnpsapi.OrderCancelReson9, OrderCancelRole: 2, } parameter.PartnerOrderCode = bill.VendorOrderID if err = api.FnAPI.CancelOrder(parameter); err != nil { if strings.Contains(err.Error(), "运单暂未生成") { err = nil } } if err != nil { return err } bill.Status = model.WaybillStatusCanceled bill.Remark = cancelReason partner.CurOrderManager.OnWaybillStatusChanged(bill) return err } func (c *DeliveryHandler) ComplaintRider(bill *model.Waybill, resonID int, resonContent string) (err error) { err = api.FnAPI.ComplaintRider(bill.VendorOrderID, bill.VendorWaybillID, complaintReson2FnResonMap[resonID], resonContent) return err } // 创建蜂鸟配送订单 func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, maxDeliveryFee int64) (bill *model.Waybill, err error) { db := dao.GetDB() // 检查配送平台是否被禁用 vendorOrgCode, err := dao.GetVendorOrgCode(db, model.VendorIDFengNiao, "", model.VendorOrgTypeDelivery) if err != nil { return nil, err } if len(vendorOrgCode) > 0 && vendorOrgCode[0].IsOpen == model.YES { return nil, fmt.Errorf("此平台配送已被系统关闭,暂不发配送 [%v]", vendorOrgCode[0].Comment) } // 蜂鸟入参结构体 parameter := &fnpsapi.CreateOrderReqParam{ PartnerOrderCode: order.VendorOrderID, OrderType: 1, PositionSource: 3, ReceiverAddress: order.ConsigneeAddress, ReceiverLongitude: utils.Int2Float64(order.ConsigneeLng) / 1000000, ReceiverLatitude: utils.Int2Float64(order.ConsigneeLat) / 1000000, GoodsTotalAmountCent: order.SalePrice, GoodsActualAmountCent: order.ActualPayPrice, GoodsWeight: utils.Int2Float64(order.Weight) / 1000, GoodsCount: order.GoodsCount, GoodsItemList: nil, ReceiverName: order.ConsigneeName, ReceiverPrimaryPhone: order.ConsigneeMobile, OutShopCode: utils.Int2Str(order.JxStoreID), ChainStoreId: "", SerialNumber: fmt.Sprintf("%s #%d", model.VendorChineseNames[order.VendorID], order.OrderSeq), } // 重量超标减少配送费 weight := 4.9500 reallyWeight := utils.Int2Float64(order.Weight) / 1000 if reallyWeight >= weight || reallyWeight <= model.NO { parameter.GoodsWeight = weight } else { parameter.GoodsWeight = reallyWeight } var goodsList []*fnpsapi.GoodsItemsList if len(order.Skus) == model.NO { goodsList = append(goodsList, &fnpsapi.GoodsItemsList{ ItemName: "平台商品(本地暂无储存信息)", ItemQuantity: model.YES, ItemAmountCent: model.YES, ItemActualAmountCent: model.YES, ItemId: utils.Int2Str(9527), }) } else { for _, v := range order.Skus { goodsList = append(goodsList, &fnpsapi.GoodsItemsList{ ItemName: v.SkuName, ItemQuantity: v.Count, ItemAmountCent: v.SalePrice, ItemActualAmountCent: v.SalePrice, ItemId: utils.Int2Str(v.SkuID), }) } } parameter.GoodsItemList = goodsList //要求饿百的订单要传来源 if order.VendorID == model.VendorIDEBAI { parameter.OrderSource = "109" } // 创建蜂鸟订单,运单id fnOrderId, err := api.FnAPI.CreateOrder(parameter) if err != nil { return nil, err } // 查询订单获取配送费 desireFee, actualFee := GetDesiredFee(order.VendorOrderID) bill = &model.Waybill{ VendorOrderID: order.VendorOrderID, OrderVendorID: order.VendorID, VendorWaybillID: fnOrderId, VendorWaybillID2: order.VendorOrderID, WaybillVendorID: model.VendorIDFengNiao, DesiredFee: desireFee, ActualFee: actualFee, } delivery.OnWaybillCreated(bill) return bill, err } // 预下单获取配送费 func (c *DeliveryHandler) GetWaybillFee(order *model.GoodsOrder) (deliveryFeeInfo *partner.WaybillFeeInfo, err error) { preCreateOrder := &fnpsapi.PreCreateOrder{ PartnerOrderCode: order.VendorOrderID, OrderType: 1, PositionSource: 3, ReceiverAddress: order.ConsigneeAddress, ReceiverLongitude: utils.Int2Float64(order.ConsigneeLng) / 1000000, ReceiverLatitude: utils.Int2Float64(order.ConsigneeLat) / 1000000, GoodsTotalAmountCent: order.SalePrice, GoodsActualAmountCent: order.ActualPayPrice, GoodsWeight: utils.Int2Float64(order.Weight) / 1000, GoodsCount: order.GoodsCount, GoodsItemList: nil, OutShopCode: utils.Int2Str(order.JxStoreID), } // 重量超标减少配送费 weight := 4.9500 reallyWeight := utils.Int2Float64(order.Weight) / 1000 if reallyWeight >= weight || reallyWeight <= model.NO { preCreateOrder.GoodsWeight = weight } else { preCreateOrder.GoodsWeight = reallyWeight } var goodsList []*fnpsapi.GoodsItemsList if len(order.Skus) == model.NO { goodsList = append(goodsList, &fnpsapi.GoodsItemsList{ ItemName: "平台商品(本地暂无储存信息)", ItemQuantity: model.YES, ItemAmountCent: model.YES, ItemActualAmountCent: model.YES, ItemId: utils.Int2Str(9527), }) } else { for _, v := range order.Skus { goodsList = append(goodsList, &fnpsapi.GoodsItemsList{ ItemName: v.SkuName, ItemQuantity: v.Count, ItemAmountCent: v.SalePrice, ItemActualAmountCent: v.SalePrice, ItemId: utils.Int2Str(v.SkuID), }) } } preCreateOrder.GoodsItemList = goodsList deliveryFeeInfo = &partner.WaybillFeeInfo{} deliveryFeeInfo.RefDeliveryFee, deliveryFeeInfo.RefAddFee, err = api.FnAPI.PreCreateByShopFn(preCreateOrder) // deliveryFeeInfo.DeliveryFee = deliveryFeeInfo.RefDeliveryFee return deliveryFeeInfo, err } // 订单状态 func OnWaybillMsg(msg *fnpsapi.OrderStatusNottify, resultParam *fnpsapi.ShortStatus) (resp *fnpsapi.CallbackResponse) { cc := &fnpsapi.OrderCallbackParam{} if err := utils.Map2StructByJson(msg.Param, cc, true); err != nil { return fnpsapi.Err2CallbackResponse(err, "") } // 多次取消,只处理第一次 if cc.OrderStatus == fnpsapi.OrderStatusAcceptCacle { bill, err := partner.CurOrderManager.LoadWaybill(utils.Int64ToStr(cc.OrderId), model.VendorIDFengNiao) if err != nil { return fnpsapi.Err2CallbackResponse(err, "") } if bill.Status == model.OrderStatusCanceled { return fnpsapi.Err2CallbackResponse(nil, "") } } var good *model.GoodsOrder sql := `SELECT * FROM goods_order WHERE vendor_order_id = ? ORDER BY order_created_at DESC LIMIT 1 OFFSET 0` sqlParams := []interface{}{cc.PartnerOrderCode} dao.GetRow(dao.GetDB(), &good, sql, sqlParams) if good == nil || good.VendorOrderID == "" { _, err := fnpsapi.HttpToGuoYuanFN(utils.Struct2MapByJson(resultParam), "order") if err != nil { return fnpsapi.Err2CallbackResponse(err, "") } return fnpsapi.Err2CallbackResponse(nil, "") } order := &model.Waybill{ VendorWaybillID: utils.Int64ToStr(cc.OrderId), VendorWaybillID2: cc.PartnerOrderCode, WaybillVendorID: model.VendorIDFengNiao, CourierName: cc.CarrierDriverName, CourierMobile: cc.CarrierDriverPhone, VendorStatus: utils.Int2Str(cc.OrderStatus), StatusTime: utils.Timestamp2Time(cc.PushTime), Remark: cc.Description, } if cc.PushTime == 0 { order.StatusTime = time.Now() } order.VendorOrderID, order.OrderVendorID = jxutils.SplitUniversalOrderID(cc.PartnerOrderCode) order.OrderVendorID = good.VendorID store, _ := dao.GetStoreDetail(dao.GetDB(), good.JxStoreID, good.VendorID, good.VendorOrgCode) orderStatus := utils.Str2Int64(order.VendorStatus) switch orderStatus { case fnpsapi.OrderStatusAcceptCreate, fnpsapi.OrderStatusAccept: // 0 创建订单 order.DesiredFee, order.ActualFee = GetDesiredFee(order.VendorOrderID) order.DesiredFee += int64(store.FreightMarkup) // 运营加价 order.Status = model.WaybillStatusNew //5 带调度 case fnpsapi.OrderStatusAssigned: //20分配骑手 order.DesiredFee, order.ActualFee = GetDesiredFee(order.VendorOrderID) order.DesiredFee += int64(store.FreightMarkup) order.Status = model.WaybillStatusCourierAssigned //12 order.Remark = order.CourierName + "," + order.CourierMobile case fnpsapi.OrderStatusArrived: // 80 到店 //order.DesiredFee, order.ActualFee = GetDesiredFee(order.VendorOrderID) order.Status = model.WaybillStatusCourierArrived case fnpsapi.OrderStatusDelivering: // 2 配送中 order.Status = model.WaybillStatusDelivering case fnpsapi.OrderStatusDelivered: // 3 已经送达 order.Status = model.WaybillStatusDelivered case fnpsapi.OrderStatusAcceptCacle: // 4取消订单 order.Status = model.WaybillStatusCanceled order.VendorStatus = utils.Int2Str(fnpsapi.OrderStatusAcceptCacle) case fnpsapi.OrderStatusException: // 5 异常 order.Status = model.WaybillStatusCanceled // 22 order.VendorStatus = utils.Int2Str(fnpsapi.OrderStatusException) default: globals.SugarLogger.Warnf("onWaybillMsg unknown msg:%v", msg) } if err := partner.CurOrderManager.OnWaybillStatusChanged(order); err != nil { return fnpsapi.Err2CallbackResponse(err, "") } switch order.OrderVendorID { case model.VendorIDDD: Lng, Lat, _ := partner.GetRidderPositionGetter(order.WaybillVendorID).GetRidderPosition(nil, order.VendorOrderID, order.VendorOrderID, order.VendorWaybillID, order.VendorWaybillID2) tiktokStatusPush(order, orderStatus, utils.Float64ToStr(Lng), utils.Float64ToStr(Lat), order.VendorOrgCode) case model.VendorIDMTWM, model.VendorIDTaoVegetable: delivery.GetOrderRiderInfoToPlatform(order.VendorOrderID, order.Status) // 骑手位置更新 if good.VendorID == model.VendorIDTaoVegetable && cc.OrderStatus == fnpsapi.OrderStatusAssigned { tao_vegetable.PushDelivererChangeInfo(good, order, tao.TaoDeliveryTypeFN) } } return fnpsapi.Err2CallbackResponse(nil, "") } // 抖音订单状态回传 func tiktokStatusPush(order *model.Waybill, orderStatus int64, lng, lat, vendorOrgCode string) { result := &utils.RiderInfo{ OrderId: order.VendorOrderID, ThirdCarrierOrderId: order.VendorOrderID, CourierName: order.CourierName, CourierPhone: order.CourierMobile, LogisticsStatus: order.Status, OpCode: "", LogisticsProviderCode: utils.FnPsCode, Longitude: lng, Latitude: lat, } switch orderStatus { case fnpsapi.OrderStatusAcceptCreate, fnpsapi.OrderStatusAccept: // 待接单,召唤骑手 result.LogisticsStatus = model.WaybillStatusNew result.LogisticsContext = model.RiderWaitRider case fnpsapi.OrderStatusAssigned: //20分配骑手 待取货 result.LogisticsStatus = model.WaybillStatusCourierAssigned // 分配骑手 result.LogisticsContext = model.RiderWaitGetGoods case fnpsapi.OrderStatusDelivering: // 2 配送中 // 配送中 result.LogisticsStatus = model.WaybillStatusDelivering result.LogisticsContext = model.RiderGetOrderDelivering case fnpsapi.OrderStatusDelivered: // 3 已经送达 // 完成 result.LogisticsStatus = model.WaybillStatusDelivered result.LogisticsContext = model.RiderGetOrderDelivered case fnpsapi.OrderStatusAcceptCacle: // 取消 result.LogisticsStatus = model.WaybillStatusCanceled result.LogisticsContext = model.RiderGetOrderCanceled case fnpsapi.OrderStatusException: // 5 异常: // 配送异常返回值 result.LogisticsStatus = model.WaybillStatusDeliverFailed result.LogisticsContext = model.RiderGetOrderDeliverFailed case fnpsapi.OrderStatusArrived: // 80 到店 // 骑手到店 result.LogisticsStatus = model.WaybillStatusCourierArrived result.LogisticsContext = model.RiderToStore default: result.LogisticsStatus = 0 result.LogisticsContext = model.RiderGetOrderDeliverOther } delivery.PullTiktokRiderInfo(result, vendorOrgCode) } // 异常报备 func OnWaybillExceptFn(msg *fnpsapi.AbnormalReportNotify) (retVal *fnpsapi.CallbackResponse) { return CurDeliveryHandler.OnWaybillExcept(msg) } func (c *DeliveryHandler) OnWaybillExcept(msg *fnpsapi.AbnormalReportNotify) (retVal *fnpsapi.CallbackResponse) { jxutils.CallMsgHandler(func() { order := &model.Waybill{ VendorWaybillID: msg.PartnerOrderCode, VendorWaybillID2: utils.Int64ToStr(msg.OrderId), WaybillVendorID: model.VendorIDFengNiao, CourierName: string(msg.CarrierDriverId), CourierMobile: "", Status: model.WaybillStatusUnknown, // todo 这里要再确定一下是否只要收到订单异常消息就只简单当成一个消息 VendorStatus: msg.AbnormalCode, StatusTime: utils.Timestamp2Time(msg.AbnormalReportTime), } order.VendorOrderID, order.OrderVendorID = jxutils.SplitUniversalOrderID(msg.PartnerOrderCode) retVal = fnpsapi.Err2CallbackResponse(partner.CurOrderManager.OnWaybillStatusChanged(order), "fn OnWaybillExcept") }, jxutils.ComposeUniversalOrderID(msg.PartnerOrderCode, model.VendorIDFengNiao)) return retVal } // 查询订单配送费(蜂鸟加4毛) func GetDesiredFee(vendorOrderID string) (desiredFee, acuteFee int64) { if result, err := api.FnAPI.QueryOrder(vendorOrderID); err == nil { return result.OrderTotalAmountCent, result.OrderActualAmountCent } return desiredFee, acuteFee } // 获取骑手信息 func (c *DeliveryHandler) GetRiderInfo(orderId string, deliveryId int64, mtPeisongId string) (rider *utils.RiderInfo, err error) { // 获取订单状态 order, err := api.FnAPI.QueryOrder(orderId) if err != nil { return nil, err } // 获取骑手坐标 knightInfo, err := api.FnAPI.GetKnightInfo(&fnpsapi.GetOrderDetailReq{PartnerOrderCode: orderId}) if err != nil { return nil, err } result := &utils.RiderInfo{ OrderId: orderId, ThirdCarrierOrderId: utils.Int64ToStr(order.TrackingId), CourierName: knightInfo.CarrierDriverName, CourierPhone: knightInfo.CarrierDriverPhone, LogisticsProviderCode: utils.FnPsCode, LogisticsStatus: order.OrderStatus, // 默认正在配送中 Latitude: knightInfo.CarrierDriverLatitude, Longitude: knightInfo.CarrierDriverLongitude, } switch order.OrderStatus { case 0: // 新订单 result.LogisticsStatus = model.WaybillStatusNew result.LogisticsContext = model.RiderWaitRider case 20: // 骑手接单 result.LogisticsStatus = model.WaybillStatusCourierAssigned result.LogisticsContext = model.RiderGetOrder case 80: // 骑手到店 result.LogisticsStatus = model.WaybillStatusCourierArrived result.LogisticsContext = model.RiderToStore case 2: // 配送中 result.LogisticsStatus = model.WaybillStatusDelivering result.LogisticsContext = model.RiderGetOrderDelivering case 3: // 完成 result.LogisticsStatus = model.WaybillStatusDelivered result.LogisticsContext = model.RiderGetOrderDelivered case 4: // 取消 result.LogisticsStatus = model.WaybillStatusCanceled result.LogisticsContext = model.RiderGetOrderCanceled case 5: // 配送异常 result.LogisticsStatus = model.WaybillStatusDeliverFailed result.LogisticsContext = model.RiderGetOrderDeliverFailed default: result.LogisticsStatus = 0 result.LogisticsContext = model.RiderGetOrderDeliverOther } return result, nil } // GetDeliverLiquidatedDamages 获取运单取消违约金 // 蜂鸟:骑手接单后-取餐之前,每单扣除2元,超过20分钟不扣款 func (c *DeliveryHandler) GetDeliverLiquidatedDamages(orderId string, deliverId string) (money int64, err error) { // 获取订单状态 order, err := api.FnAPI.QueryOrder(orderId) if err != nil { return 0, err } // 本地状态兑换金额 var localPrice int64 = 0 orderStatus, err := orderman.FixedOrderManager.GetWayBillStatusList(orderId, deliverId, model.VendorIDFengNiao) if err != nil { return 0, err } isMerchantCancel := false // 获取发起取消的人员 for _, v := range orderStatus { if v.VendorStatus == utils.Int64ToStr(model.WaybillStatusCancel) { isMerchantCancel = true // 商户取消 break } } for i := len(orderStatus) - 1; i >= 0; i-- { if orderStatus[i].VendorStatus == utils.Int2Str(fnpsapi.OrderStatusAcceptCacle) { continue } if orderStatus[i].VendorStatus == utils.Int2Str(fnpsapi.OrderStatusArrived) || orderStatus[i].VendorStatus == utils.Int2Str(fnpsapi.OrderStatusDelivering) || orderStatus[i].VendorStatus == utils.Int2Str(fnpsapi.OrderStatusDelivered) { if isMerchantCancel { localPrice = order.OrderTotalAmountCent } else { localPrice = 0 } break } if orderStatus[i].VendorStatus == utils.Int2Str(fnpsapi.OrderStatusAssigned) { nowTime := time.Now().Unix() fetchTime := orderStatus[i].StatusTime.Unix() timeDiffer := nowTime - fetchTime if timeDiffer > 15*60 || timeDiffer < 60 { localPrice = 0 } else { localPrice = 200 } break } if orderStatus[i].VendorStatus == utils.Int2Str(fnpsapi.OrderStatusAcceptCreate) || orderStatus[i].VendorStatus == utils.Int2Str(fnpsapi.OrderStatusAccept) { localPrice = 0 break } } // 已经分配骑手,且超过十五分钟,不扣款 var vendorPrice int64 = 0 if len(order.EventLogDetails) != model.NO { for i := len(order.EventLogDetails) - 1; i >= 0; i-- { switch order.EventLogDetails[i].OrderStatus { case fnpsapi.OrderStatusDelivered, fnpsapi.OrderStatusArrived, fnpsapi.OrderStatusDelivering: // 送达,到店,配送中 取消订单全额扣款 vendorPrice = order.OrderTotalAmountCent break case fnpsapi.OrderStatusAcceptCacle, fnpsapi.OrderStatusException: // 取消和异常状态,跳过查看上一状态 continue case fnpsapi.OrderStatusAcceptCreate, fnpsapi.OrderStatusAccept: // 生成运单和系统接单取消不扣除费用 vendorPrice = 0 case fnpsapi.OrderStatusAssigned: if time.Now().UnixNano()/1e6-order.EventLogDetails[i].OccurTime > fnpsapi.WayBillPressureOrderTime { return 0, nil } else { return 200, nil } } } } if localPrice > vendorPrice { return localPrice, nil } return vendorPrice, nil } func (c *DeliveryHandler) GetRidderPosition(ctx *jxcontext.Context, vendorOrgCode, vendorOrderID, vendorWaybillID, vendorWaybillID2 string) (lng, lat float64, err error) { order, err := api.FnAPI.GetKnightInfo(&fnpsapi.GetOrderDetailReq{ PartnerOrderCode: vendorOrderID, }) if err == nil { lng = utils.Str2Float64WithDefault(order.CarrierDriverLongitude, 0) lat = utils.Str2Float64WithDefault(order.CarrierDriverLatitude, 0) } return lng, lat, err } func (c *DeliveryHandler) UpdateWaybillTip(ctx *jxcontext.Context, vendorOrgCode, vendorStoreID, vendorOrderID, vendorWaybillID, vendorWaybillID2, cityCode string, tipFee int64) (err error) { return api.FnAPI.AddTip(&fnpsapi.AddTipRes{ OrderId: "", PartnerOrderCode: vendorOrderID, AddTipAmountCent: tipFee, ThirdIndexId: time.Now().Unix(), }) } func (c *DeliveryHandler) GetWaybillTip(ctx *jxcontext.Context, vendorOrgCode, vendorStoreID, vendorOrderID, vendorWaybillID, vendorWaybillID2 string) (tipFee int64, err error) { order, err := api.FnAPI.QueryOrder(vendorOrderID) if err == nil { tipFee = order.OrderTipAmountCent } return tipFee, err }