Files
jx-callback/business/partner/delivery/sfps/waybill.go
2023-05-29 15:10:44 +08:00

434 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 sfps
import (
"errors"
"fmt"
"time"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/business/partner/delivery"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/baseapi/platformapi/mtpsapi"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/globals/api"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/baseapi/platformapi/sfps2"
"git.rosy.net.cn/jx-callback/business/model"
)
type DeliveryHandler struct {
}
var (
curDeliveryHandler *DeliveryHandler
)
func init() {
if api.SfPsAPI != nil {
curDeliveryHandler = new(DeliveryHandler)
partner.RegisterDeliveryPlatform(curDeliveryHandler, true)
}
}
func (d DeliveryHandler) GetVendorID() int {
return model.VendorIDSFPS
}
func (d DeliveryHandler) CreateStore(ctx *jxcontext.Context, storeDetail *dao.StoreDetail2) (vendorStoreID string, status int, err error) {
return "", 0, fmt.Errorf("顺丰派送暂不支持此操作")
}
func (d DeliveryHandler) GetStore(ctx *jxcontext.Context, storeID int, vendorStoreID string) (storeDetail *dao.StoreDetail2, err error) {
store, err := dao.GetStoreDetail(dao.GetDB(), utils.Str2Int(vendorStoreID), model.VendorIDSFPS, "")
if err == nil {
storeDetail = &dao.StoreDetail2{
Store: model.Store{
Name: store.Name,
Tel1: store.Tel1,
Address: store.Address,
Lat: store.Lat,
Lng: store.Lng,
},
VendorID: model.VendorIDSFPS,
VendorStoreID: utils.Int2Str(storeID),
CourierStatus: model.StoreStatusOpened,
}
}
return storeDetail, err
}
func (d DeliveryHandler) IsErrStoreNotExist(err error) bool {
return false
}
func (d DeliveryHandler) IsErrStoreExist(err error) bool {
return false
}
func (d DeliveryHandler) CreateWaybill(order *model.GoodsOrder, maxDeliveryFee int64) (bill *model.Waybill, err error) {
vendorOrgCode, err := dao.GetVendorOrgCode(dao.GetDB(), model.VendorIDSFPS, "", 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)
}
store, err := dao.GetStoreDetail(dao.GetDB(), getReallyStoreID(order.StoreID, order.JxStoreID), 0, "")
if err != nil {
return nil, err
}
var (
weight int
productDetail []*sfps2.ProductDetail
)
if order.Weight >= 49500 {
weight = 49500
} else {
weight = order.Weight
}
param := &sfps2.CreateOrderReq{
ShopId: sfps2.SFShopStoreID,
ShopOrderId: order.VendorOrderID,
OrderSource: GetVendorSource(order.VendorID),
OrderTime: order.CreatedAt.Unix(),
Receive: &sfps2.ReceiveAddress{
UserName: order.ConsigneeName,
UserPhone: order.ConsigneeMobile,
UserAddress: order.ConsigneeAddress,
UserLat: utils.Float64ToStr(jxutils.IntCoordinate2Standard(order.ConsigneeLat)),
UserLng: utils.Float64ToStr(jxutils.IntCoordinate2Standard(order.ConsigneeLng)),
},
Shop: &sfps2.SfShopInfo{
ShopName: order.StoreName,
ShopPhone: store.Tel1,
ShopAddress: store.Address,
ShopLat: utils.Float64ToStr(jxutils.IntCoordinate2Standard(store.Lat)),
ShopLng: utils.Float64ToStr(jxutils.IntCoordinate2Standard(store.Lng)),
},
OrderDetail: &sfps2.OrderDetail{
TotalPrice: order.ActualPayPrice,
ProductType: sfps2.ProductTypeFresh,
WeightGram: int64(weight),
ProductNum: int64(order.GoodsCount),
ProductTypeNum: int64(order.SkuCount),
},
}
for _, v := range order.Skus {
productDetail = append(productDetail, &sfps2.ProductDetail{
ProductName: v.SkuName,
ProductNum: int64(v.Count),
})
}
param.OrderDetail.ProductDetail = productDetail
sfOrderID, sfBillID, sfTotalPrice, sfReallyPrice, err := api.SfPsAPI.CreateOrder(param)
if err != nil {
return nil, err
}
//+0.2
desiredFee := utils.Float64TwoInt(sfTotalPrice) + utils.WayBillDeliveryMarkUp
bill = &model.Waybill{
VendorOrderID: order.VendorOrderID,
OrderVendorID: order.VendorID,
VendorWaybillID: sfOrderID,
VendorWaybillID2: sfBillID,
WaybillVendorID: model.VendorIDFengNiao,
DesiredFee: int64(desiredFee),
ActualFee: utils.Float64TwoInt64(sfReallyPrice),
}
delivery.OnWaybillCreated(bill)
return bill, err
}
func (d DeliveryHandler) CancelWaybill(bill *model.Waybill, cancelReasonID int, cancelReason string) (err error) {
if err = api.SfPsAPI.CancelOrder(bill.VendorWaybillID); err != nil {
return err
}
bill.Status = model.WaybillStatusCanceled
bill.Remark = cancelReason
partner.CurOrderManager.OnWaybillStatusChanged(bill)
return nil
}
func (d DeliveryHandler) GetWaybillFee(order *model.GoodsOrder) (deliveryFeeInfo *partner.WaybillFeeInfo, err error) {
var (
weight int
//productDetail []*sfps2.ProductDetail
)
if order.Weight >= 49500 {
weight = 49500
} else {
weight = order.Weight
}
store, err := dao.GetStoreDetail(dao.GetDB(), getReallyStoreID(order.StoreID, order.JxStoreID), 0, "")
if err != nil {
return nil, err
}
param := &sfps2.PreCreateOrderReq{
ShopId: sfps2.SFShopStoreID,
UserLng: utils.Float64ToStr(jxutils.IntCoordinate2Standard(order.ConsigneeLng)),
UserLat: utils.Float64ToStr(jxutils.IntCoordinate2Standard(order.ConsigneeLat)),
UserAddress: order.ConsigneeAddress,
Weight: int64(weight),
ProductType: sfps2.ProductTypeFresh,
ShopType: sfps2.OrderTypeSF,
LbsType: sfps2.LbsTypeGD,
RiderPickMethod: sfps2.RiderPickUpMethodSTU,
Shop: &sfps2.SfShopInfo{
ShopName: order.StoreName,
ShopPhone: store.Tel1,
ShopAddress: store.Address,
ShopLat: utils.Float64ToStr(jxutils.IntCoordinate2Standard(store.Lat)),
ShopLng: utils.Float64ToStr(jxutils.IntCoordinate2Standard(store.Lng)),
},
}
deliveryFeeInfo = &partner.WaybillFeeInfo{}
price, err := api.SfPsAPI.PreCreateOrder(param)
//+0.2
deliveryFee := utils.Float64TwoInt(price) + utils.WayBillDeliveryMarkUp
globals.SugarLogger.Debugf("GetWaybillFee utils.Float64TwoInt(price)=%d", utils.Float64TwoInt(price))
globals.SugarLogger.Debugf("GetWaybillFee utils.WayBillDeliveryMarkUp=%d", utils.WayBillDeliveryMarkUp)
globals.SugarLogger.Debugf("GetWaybillFee deliveryFee=%d", deliveryFee)
deliveryFeeInfo.DeliveryFee = int64(deliveryFee)
globals.SugarLogger.Debugf("GetWaybillFee deliveryFeeInfo.DeliveryFee=%d", deliveryFeeInfo.DeliveryFee)
return deliveryFeeInfo, err
}
func (d DeliveryHandler) ComplaintRider(bill *model.Waybill, resonID int, resonContent string) (err error) {
return nil
}
// GetDeliverLiquidatedDamages 获取取消运单违约金
func (d DeliveryHandler) GetDeliverLiquidatedDamages(orderId string, deliverId string) (money int64, err error) {
waybill, err := dao.GetWaybills(dao.GetDB(), orderId, []int64{model.VendorIDSFPS})
if len(waybill) == 0 || err != nil {
return 0, err
}
deductionFee, err := api.SfPsAPI.PreCancelOrder(waybill[0].VendorWaybillID)
if deductionFee == 0 || err != nil {
return 0, err
}
money = utils.Float64TwoInt64(deductionFee)
return money, nil
}
func (d DeliveryHandler) GetRiderInfo(orderId string, deliveryId int64, mtPeisongId string) (rider *mtpsapi.RiderInfo, err error) {
order, err := dao.GetWaybills(dao.GetDB(), orderId, []int64{model.VendorIDSFPS})
if len(order) == 0 || err != nil {
return nil, errors.New("顺丰 订单id无效请检查")
}
//获取顺丰运单详情
sfOrder, err := api.SfPsAPI.GetOrderStatus(order[0].VendorWaybillID)
//获取顺丰骑手实时位置
sfRider, err := api.SfPsAPI.GetRiderLatestPosition(sfOrder.OrderID)
result := &mtpsapi.RiderInfo{
OrderId: orderId,
ThirdCarrierOrderId: sfOrder.OrderID,
CourierName: sfOrder.RiderName,
CourierPhone: sfOrder.RiderPhone,
LogisticsProviderCode: mtpsapi.SFPSCode,
LogisticsStatus: utils.Float64TwoInt(sfOrder.OrderStatus), // 默认正在配送中
Latitude: sfRider.RiderLat,
Longitude: sfRider.RiderLng,
}
switch sfOrder.OrderStatus {
case sfps2.OrderStatusNewOrder:
result.LogisticsStatus = model.WaybillStatusNew
result.LogisticsContext = model.RiderWaitRider
case sfps2.OrderStatusTakeOrder:
result.LogisticsStatus = model.WaybillStatusCourierAssigned
result.LogisticsContext = model.RiderGetOrder
case sfps2.OrderStatusArrivedStore:
result.LogisticsStatus = model.WaybillStatusCourierArrived
result.LogisticsContext = model.RiderToStore
case sfps2.OrderStatusRiderArriving:
result.LogisticsStatus = model.WaybillStatusDelivering
result.LogisticsContext = model.RiderGetOrderDelivering
case sfps2.OrderStatusFinished:
result.LogisticsStatus = model.WaybillStatusDelivered
result.LogisticsContext = model.RiderGetOrderDelivered
case sfps2.OrderStatusOrderCancel:
result.LogisticsStatus = model.WaybillStatusCanceled
result.LogisticsContext = model.RiderGetOrderCanceled
case sfps2.OrderStatusError:
result.LogisticsStatus = model.WaybillStatusDeliverFailed
result.LogisticsContext = model.RiderGetOrderDeliverFailed
default:
result.LogisticsStatus = 0
result.LogisticsContext = model.RiderGetOrderDeliverOther
}
return result, nil
}
// OnWaybillMsg 配送状态更改回调
func OnWaybillMsg(msg *sfps2.RiderStatus) (resp *sfps2.CallbackResponse) {
order := &model.Waybill{
VendorWaybillID: msg.SFOrderID,
WaybillVendorID: model.VendorIDSFPS,
VendorOrderID: msg.ShopOrderID,
CourierName: msg.OperatorName,
CourierMobile: msg.OperatorPhone,
VendorStatus: utils.Int2Str(msg.OrderStatus),
StatusTime: utils.Timestamp2Time(int64(msg.PushTime)),
Remark: msg.StatusDesc,
}
if msg.PushTime == 0 {
order.StatusTime = time.Now()
}
//获取实时订单信息
sfOrder, err := api.SfPsAPI.GetOrderStatus(msg.SFOrderID)
if err != nil {
return sfps2.Err2CallbackResponse(err)
}
//+0.2
sfTotalPrice := utils.Float64TwoInt(sfOrder.TotalPrice) + utils.WayBillDeliveryMarkUp
sfActualPrice := utils.Float64TwoInt64(sfOrder.RealPayMoney)
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{}{msg.ShopOrderID}
dao.GetRow(dao.GetDB(), &good, sql, sqlParams)
order.OrderVendorID = good.VendorID
orderStatus := utils.Str2Int64(order.VendorStatus)
switch orderStatus {
case sfps2.OrderStatusNewOrder: //1订单创建
order.DesiredFee = int64(sfTotalPrice)
order.ActualFee = sfActualPrice
order.Status = model.WaybillStatusNew //5 带调度
case sfps2.OrderStatusTakeOrder: //10配送员接单
order.DesiredFee = int64(sfTotalPrice)
order.Status = model.WaybillStatusCourierAssigned //12
order.Remark = order.CourierName + "" + order.CourierMobile
case sfps2.OrderStatusArrivedStore:
order.DesiredFee = int64(sfTotalPrice)
order.Status = model.WaybillStatusCourierArrived
case sfps2.OrderStatusRiderArriving:
order.DesiredFee = int64(sfTotalPrice)
order.Status = model.WaybillStatusDelivering
case sfps2.OrderStatusFinished:
order.DesiredFee = int64(sfTotalPrice)
order.Status = model.WaybillStatusDelivered
case sfps2.OrderStatusOrderCancel:
order.Status = model.WaybillStatusCanceled
case sfps2.OrderStatusError:
order.Status = model.WaybillStatusDeliverFailed // 22
default:
globals.SugarLogger.Warnf("onWaybillMsg unknown msg:%v", msg)
}
if err := partner.CurOrderManager.OnWaybillStatusChanged(order); err != nil {
return sfps2.Err2CallbackResponse(err)
}
if order.OrderVendorID == model.VendorIDDD {
result := &mtpsapi.RiderInfo{
OrderId: order.VendorOrderID,
ThirdCarrierOrderId: order.VendorOrderID,
CourierName: order.CourierName,
CourierPhone: order.CourierMobile,
LogisticsProviderCode: "10002",
LogisticsStatus: order.Status,
OpCode: "",
}
switch orderStatus {
case sfps2.OrderStatusNewOrder:
result.LogisticsStatus = model.WaybillStatusNew
result.LogisticsContext = model.RiderWaitRider
case sfps2.OrderStatusTakeOrder:
result.LogisticsStatus = model.WaybillStatusCourierAssigned // 分配骑手
result.LogisticsContext = model.RiderWaitGetGoods
case sfps2.OrderStatusRiderArriving:
result.LogisticsStatus = model.WaybillStatusDelivering
result.LogisticsContext = model.RiderGetOrderDelivering
case sfps2.OrderStatusFinished:
result.LogisticsStatus = model.WaybillStatusDelivered
result.LogisticsContext = model.RiderGetOrderDelivered
case sfps2.OrderStatusOrderCancel:
result.LogisticsStatus = model.WaybillStatusCanceled
result.LogisticsContext = model.RiderGetOrderCanceled
case sfps2.OrderStatusError:
result.LogisticsStatus = model.WaybillStatusDeliverFailed
result.LogisticsContext = model.RiderGetOrderDeliverFailed
case sfps2.OrderStatusArrivedStore:
result.LogisticsStatus = model.WaybillStatusCourierArrived
result.LogisticsContext = model.RiderToStore
default:
result.LogisticsStatus = 0
result.LogisticsContext = model.RiderGetOrderDeliverOther
}
delivery.PullTiktokRiderInfo(result)
}
defer delivery.GetOrderRiderInfoToPlatform(order.VendorOrderID, order.Status) // 骑手位置更新
return sfps2.Err2CallbackResponse(nil)
}
// OnWaybillExceptSF 异常报备
func OnWaybillExceptSF(msg *sfps2.RiderException) (retVal *sfps2.CallbackResponse) {
jxutils.CallMsgHandler(func() {
order := &model.Waybill{
VendorWaybillID: msg.SFOrderID,
VendorWaybillID2: utils.Int2Str(msg.ExID),
WaybillVendorID: model.VendorIDSFPS,
CourierName: msg.OperatorName,
CourierMobile: "",
Status: model.WaybillStatusUnknown, // todo 这里要再确定一下是否只要收到订单异常消息就只简单当成一个消息
VendorStatus: utils.Int2Str(msg.OrderStatus),
StatusTime: utils.Timestamp2Time(int64(msg.PushTime)),
}
order.VendorOrderID, order.OrderVendorID = jxutils.SplitUniversalOrderID(msg.ShopOrderID)
retVal = sfps2.Err2CallbackResponse(partner.CurOrderManager.OnWaybillStatusChanged(order))
}, jxutils.ComposeUniversalOrderID(msg.ShopOrderID, model.VendorIDSFPS))
return retVal
}
// GetVendorSource 辅助函数
//获取订单来源标识符
func GetVendorSource(vendorID int) (source string) {
switch vendorID {
case model.VendorIDMTWM:
source = sfps2.OrderSourceMt
case model.VendorIDELM:
source = sfps2.OrderSourceELM
case model.VendorIDEBAI:
source = sfps2.OrderSourceEBAI
case model.VendorIDDD:
source = "抖音"
case model.VendorIDJD:
source = "京东"
case model.VendorIDJX:
source = "京西"
default:
source = "其他"
}
return source
}
func getReallyStoreID(storeID, jxStoreID int) int {
if storeID == 0 && jxStoreID == 0 {
return 0
}
if storeID == 0 {
return jxStoreID
} else {
return storeID
}
}