Files
jx-callback/business/partner/delivery/dada/waybill.go
2020-08-12 12:02:54 +08:00

400 lines
14 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 dada
import (
"errors"
"fmt"
"git.rosy.net.cn/baseapi/platformapi/dadaapi"
"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/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/business/partner/delivery"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
"github.com/astaxie/beego"
)
const (
maxOrderPrice = 6399 // 单位为分,达达最大价格,超过这个价格配送费会增加
maxOrderWeight = 5000 // 5公斤
)
var (
ErrCanNotFindDadaCityCode = errors.New("不能找到美团配送站点配置")
ErrExceedMaxDiffFee2Mtps = errors.New("与美团配送超价太多")
)
var (
CurDeliveryHandler *DeliveryHandler
dadaDistrictMap = map[string]string{
"苏州工业园区": "工业园区",
"郫都区": "郫县",
"管城回族区": "管城区",
"昆山市": "1",
"常熟市": "1",
"太仓市": "1",
"虞山街道": "虞山镇",
"常福街道": "虞山镇",
}
)
type DeliveryHandler struct {
}
func init() {
CurDeliveryHandler = new(DeliveryHandler)
partner.RegisterDeliveryPlatform(CurDeliveryHandler, true)
}
func OnWaybillMsg(msg *dadaapi.CallbackMsg) (retVal *dadaapi.CallbackResponse) {
return CurDeliveryHandler.OnWaybillMsg(msg)
}
func (c *DeliveryHandler) GetVendorID() int {
return model.VendorIDDada
}
func (c *DeliveryHandler) OnWaybillMsg(msg *dadaapi.CallbackMsg) (retVal *dadaapi.CallbackResponse) {
jxutils.CallMsgHandler(func() {
retVal = c.onWaybillMsg(msg)
}, jxutils.ComposeUniversalOrderID(msg.OrderID, model.VendorIDDada))
return retVal
}
func (c *DeliveryHandler) onWaybillMsg(msg *dadaapi.CallbackMsg) (retVal *dadaapi.CallbackResponse) {
order := c.callbackMsg2Waybill(msg)
switch msg.OrderStatus {
case dadaapi.OrderStatusWaitingForAccept:
if dadaOrder, err := api.DadaAPI.QueryOrderInfo(msg.OrderID); err == nil {
order.ActualFee = jxutils.StandardPrice2Int(dadaOrder.ActualFee)
order.DesiredFee = jxutils.StandardPrice2Int(dadaOrder.DeliveryFee)
}
order.Status = model.WaybillStatusNew
case dadaapi.OrderStatusAccepted:
order.Status = model.WaybillStatusAccepted
case dadaapi.OrderStatusDelivering:
order.Status = model.WaybillStatusDelivering
case dadaapi.OrderStatusFinished:
order.Status = model.WaybillStatusDelivered
case dadaapi.OrderStatusCanceled, dadaapi.OrderStatusExpired:
order.Status = model.WaybillStatusCanceled
case dadaapi.OrderStatusAddOrderFailed:
order.Status = model.WaybillStatusFailed
default:
order.Status = model.WaybillStatusUnknown
}
return dadaapi.Err2CallbackResponse(partner.CurOrderManager.OnWaybillStatusChanged(order), utils.Int2Str(order.Status))
}
func (c *DeliveryHandler) callbackMsg2Waybill(msg *dadaapi.CallbackMsg) (retVal *model.Waybill) {
retVal = &model.Waybill{
VendorWaybillID: msg.ClientID,
WaybillVendorID: model.VendorIDDada,
CourierName: msg.DmName,
CourierMobile: msg.DmMobile,
VendorStatus: utils.Int2Str(msg.OrderStatus),
Remark: msg.CancelReason,
// StatusTime: utils.Timestamp2Time(int64(msg.UpdateTime)),
}
// dada太扯了不同消息过来的时间格式不一样
updateTime := int64(msg.UpdateTime)
if updateTime > 2511789475 {
updateTime = updateTime / 1000
}
retVal.StatusTime = utils.Timestamp2Time(updateTime)
retVal.VendorOrderID, retVal.OrderVendorID = jxutils.SplitUniversalOrderID(msg.OrderID)
return retVal
}
func StoreDetail2ShopInfo(storeDetail *dao.StoreDetail2) (shopInfo *dadaapi.ShopInfo) {
lng := jxutils.IntCoordinate2Standard(storeDetail.Lng)
lat := jxutils.IntCoordinate2Standard(storeDetail.Lat)
cityName := storeDetail.CityName
districtName := storeDetail.DistrictName
if dadaDistrictMap[districtName] != "" {
if dadaDistrictMap[districtName] == "1" { // 区镇信息
cityName = districtName
districtName, _ = api.AutonaviAPI.GetCoordinateTownInfo(lng, lat)
}
if dadaDistrictMap[districtName] != "" {
districtName = dadaDistrictMap[storeDetail.DistrictName]
}
}
shopInfo = &dadaapi.ShopInfo{
OriginShopID: storeDetail.VendorStoreID,
StationName: globals.StoreName + "-" + storeDetail.Name,
Business: dadaapi.BusinessTypeConvStore, // 故意设置成这个的
CityName: cityName,
AreaName: districtName,
StationAddress: storeDetail.Address,
Lng: lng,
Lat: lat,
ContactName: storeDetail.PayeeName,
Phone: storeDetail.Tel1,
}
if storeDetail.CourierStatus >= model.StoreStatusClosed {
shopInfo.Status = dadaapi.ShopStatusOnline
} else {
shopInfo.Status = dadaapi.ShopStatusOffline
}
return shopInfo
}
func (c *DeliveryHandler) CreateStore(ctx *jxcontext.Context, storeDetail *dao.StoreDetail2) (vendorStoreID string, status int, err error) {
if globals.EnableStoreWrite {
vendorStoreID, err = api.DadaAPI.ShopAdd(StoreDetail2ShopInfo(storeDetail))
if err == nil {
status = model.StoreAuditStatusOnline
}
} else {
vendorStoreID = utils.Int64ToStr(jxutils.GenFakeID())
status = model.StoreAuditStatusOnline
}
return vendorStoreID, status, err
}
func (c *DeliveryHandler) UpdateStore(ctx *jxcontext.Context, storeDetail *dao.StoreDetail2) (err error) {
if globals.EnableStoreWrite {
err = api.DadaAPI.ShopUpdate(StoreDetail2ShopInfo(storeDetail))
}
return err
}
func dadaStatus2Jx(dadaStatus int) int {
if dadaStatus == dadaapi.ShopStatusOnline {
return model.StoreStatusOpened
}
return model.StoreStatusDisabled
}
func (c *DeliveryHandler) GetStore(ctx *jxcontext.Context, storeID int, vendorStoreID string) (storeDetail *dao.StoreDetail2, err error) {
shopInfo, err := api.DadaAPI.ShopDetail(vendorStoreID)
if err == nil {
storeDetail = &dao.StoreDetail2{
Store: model.Store{
Name: shopInfo.StationName,
Address: shopInfo.StationAddress,
Lng: jxutils.StandardCoordinate2Int(shopInfo.Lng),
Lat: jxutils.StandardCoordinate2Int(shopInfo.Lat),
PayeeName: shopInfo.ContactName,
Tel1: shopInfo.Phone,
},
VendorID: model.VendorIDDada,
VendorStoreID: shopInfo.OriginShopID,
CourierStatus: dadaStatus2Jx(shopInfo.Status),
AuditStatus: model.StoreAuditStatusOnline,
CityName: shopInfo.CityName,
DistrictName: shopInfo.AreaName,
}
}
return storeDetail, err
}
func (c *DeliveryHandler) IsErrStoreNotExist(err error) bool {
return dadaapi.IsErrShopNotExist(err)
}
func (c *DeliveryHandler) IsErrStoreExist(err error) bool {
return dadaapi.IsErrShopExist(err)
}
func (c *DeliveryHandler) GetWaybillFee(order *model.GoodsOrder) (deliveryFeeInfo *partner.WaybillFeeInfo, err error) {
db := dao.GetDB()
deliveryFeeInfo = &partner.WaybillFeeInfo{}
billParams, err := c.getBillParams(db, order)
if err == nil {
var result *dadaapi.CreateOrderResponse
if result, err = api.DadaAPI.QueryDeliverFee(billParams); err != nil {
return nil, err
}
deliveryFeeInfo.DeliveryFee = jxutils.StandardPrice2Int(result.Fee)
deliveryFeeInfo.RefDeliveryFee = deliveryFeeInfo.DeliveryFee
}
return deliveryFeeInfo, err
}
func (c *DeliveryHandler) getBillParams(db *dao.DaoDB, order *model.GoodsOrder) (billParams *dadaapi.OperateOrderParams, err error) {
billParams = &dadaapi.OperateOrderParams{
OriginID: jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID),
CargoPrice: jxutils.IntPrice2Standard(limitOrderPrice(order.ActualPayPrice)),
IsPrepay: 0,
ReceiverName: utils.FilterMb4(order.ConsigneeName),
ReceiverAddress: utils.FilterMb4(order.ConsigneeAddress),
ReceiverPhone: order.ConsigneeMobile,
}
if billParams.ShopNo, err = c.getDadaShopID(order, db); err == nil {
if billParams.CityCode, err = c.getDataCityCodeFromOrder(order, db); err == nil {
// storeTel := ""
// storeID := jxutils.GetSaleStoreIDFromOrder(order)
// storeDeatail, _ := dao.GetStoreDetail(db, storeID, order.VendorID)
// if storeDeatail.Tel2 != "" {
// storeTel = ",门店电话:" + storeDeatail.Tel2
// }
billParams.ReceiverLng, billParams.ReceiverLat, _ = jxutils.IntCoordinate2MarsStandard(order.ConsigneeLng, order.ConsigneeLat, order.CoordinateType)
billParams.Info = fmt.Sprintf("%s第%d号订单, %s", model.VendorChineseNames[order.VendorID], order.OrderSeq, utils.FilterMb4(order.BuyerComment+"取货失败或配送遇到问题请联系18048531223禁止未配送直接完成定单"))
billParams.CargoType = dadaapi.CargoTypeFresh
billParams.CargoWeight = float64(jxutils.IntWeight2Float(limitOrderWeight(order.Weight)))
billParams.CargoNum = order.GoodsCount
}
}
return billParams, err
}
// IDeliveryPlatformHandler
func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, maxDeliveryFee int64) (bill *model.Waybill, err error) {
db := dao.GetDB()
billParams, err := c.getBillParams(db, order)
if err == nil {
if globals.EnableStoreWrite {
// 达达要求第二次创建运单,调用函数不同。所以查找两天内有无相同订单号的运单
var waybillList []*model.Waybill
err2 := dao.GetRows(db, &waybillList, `
SELECT *
FROM waybill
WHERE waybill_created_at > DATE_ADD(NOW(), interval -2 day)
AND vendor_order_id = ?
AND waybill_vendor_id = ?
ORDER BY id DESC
`, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID), model.VendorIDDada)
var result *dadaapi.CreateOrderResponse
if err = err2; err == nil && len(waybillList) > 0 && waybillList[0].Status != model.WaybillStatusFailed {
// 再次创建
globals.SugarLogger.Debugf("CreateWaybill orderID:%s len(waybillList)=%d use ReaddOrder", order.VendorOrderID, len(waybillList))
if err = delivery.CallCreateWaybillPolicy(waybillList[0].ActualFee, maxDeliveryFee, order, model.VendorIDDada); err != nil {
return nil, err
}
// result, err = api.DadaAPI.ReaddOrder(billParams, addParams)
result, err = api.DadaAPI.ReaddOrder(billParams)
} else {
// 第一次创建
if err != nil {
globals.SugarLogger.Warnf("CreateWaybill orderID:%s error:%v", order.VendorOrderID, err)
}
if false {
result, err = api.DadaAPI.AddOrder(billParams)
} else {
if result, err = api.DadaAPI.QueryDeliverFee(billParams); err != nil {
return nil, err
}
if err = delivery.CallCreateWaybillPolicy(jxutils.StandardPrice2Int(result.Fee), maxDeliveryFee, order, model.VendorIDDada); err != nil {
return nil, err
}
err = api.DadaAPI.AddOrderAfterQuery(result.DeliveryNo)
}
}
if err == nil && result != nil {
bill = &model.Waybill{
VendorOrderID: order.VendorOrderID,
OrderVendorID: order.VendorID,
WaybillVendorID: model.VendorIDDada,
DesiredFee: jxutils.StandardPrice2Int(result.Fee),
ActualFee: jxutils.StandardPrice2Int(result.Fee),
}
delivery.OnWaybillCreated(bill)
}
} else {
err = fmt.Errorf("测试环境不能真正创建运单")
}
}
return bill, err
}
func (c *DeliveryHandler) CancelWaybill(bill *model.Waybill, cancelReasonID int, cancelReason string) (err error) {
switch cancelReasonID {
case partner.CancelWaybillReasonNotAcceptIntime:
cancelReasonID = dadaapi.ReasonIDNobodyAccept
case partner.CancelWaybillReasonSwitch2SelfFailed:
cancelReasonID = dadaapi.ReasonIDClientDontWantItAnymore
default:
cancelReasonID = dadaapi.ReasonIDOther
}
_, err = api.DadaAPI.CancelOrder(bill.VendorOrderID, cancelReasonID, cancelReason)
return err
}
func (c *DeliveryHandler) getDataCityCodeFromOrder(order *model.GoodsOrder, db *dao.DaoDB) (retVal string, err error) {
jxStoreID := jxutils.GetSaleStoreIDFromOrder(order)
sql := `
SELECT t2.tel_code
FROM store t1
JOIN place t2 on t1.city_code = t2.code
WHERE t1.id = ?
`
codeInfo := &struct {
TelCode string
}{}
if err = dao.GetRow(db, codeInfo, sql, jxStoreID); err != nil {
globals.SugarLogger.Errorf("GetDataCityCodeFromOrder can not find store info for vendorID:%d, store:%s, error:%v", order.VendorID, order.VendorStoreID, err)
if err == nil {
err = ErrCanNotFindDadaCityCode
}
return "", err
}
return codeInfo.TelCode, nil
}
func (c *DeliveryHandler) getDadaShopID(order *model.GoodsOrder, db *dao.DaoDB) (retVal string, err error) {
saleStoreID := jxutils.GetSaleStoreIDFromOrder(order)
storeCourierList, err2 := dao.GetOpenedStoreCouriersByStoreID(db, saleStoreID, model.VendorIDDada)
if err = err2; err != nil && !dao.IsNoRowsError(err) {
return "", err
}
if len(storeCourierList) == 0 {
return "", partner.ErrStoreHaveNoCourier
}
retVal = storeCourierList[0].VendorStoreID
if beego.BConfig.RunMode == "dev" {
retVal = "test_0001"
}
return retVal, nil
}
func limitOrderPrice(price int64) int64 {
if price > maxOrderPrice {
return maxOrderPrice
}
return price
}
func limitOrderWeight(weight int) int {
if weight > maxOrderWeight {
return maxOrderWeight
}
return weight
}
func (c *DeliveryHandler) ComplaintRider(bill *model.Waybill, resonID int, resonContent string) (err error) {
if globals.EnableStoreWrite {
err = api.DadaAPI.ComplaintRider(bill.VendorOrderID, resonID)
}
return err
}
func (c *DeliveryHandler) GetWaybillTip(ctx *jxcontext.Context, vendorOrgCode, vendorStoreID, vendorOrderID, vendorWaybillID, vendorWaybillID2 string) (tipFee int64, err error) {
order, err := api.DadaAPI.QueryOrderInfo(vendorOrderID)
if err == nil {
tipFee = jxutils.StandardPrice2Int(order.Tips)
}
return tipFee, err
}
func (c *DeliveryHandler) UpdateWaybillTip(ctx *jxcontext.Context, vendorOrgCode, vendorStoreID, vendorOrderID, vendorWaybillID, vendorWaybillID2, cityCode string, tipFee int64) (err error) {
if globals.EnableStoreWrite {
err = api.DadaAPI.AddTip(vendorOrderID, jxutils.IntPrice2Standard(tipFee), cityCode, "")
}
return err
}
func (c *DeliveryHandler) GetRidderPosition(ctx *jxcontext.Context, vendorOrgCode, vendorOrderID, vendorWaybillID, vendorWaybillID2 string) (lng, lat float64, err error) {
order, err := api.DadaAPI.QueryOrderInfo(vendorOrderID)
if err == nil {
lng = utils.Str2Float64WithDefault(order.TransporterLng, 0)
lat = utils.Str2Float64WithDefault(order.TransporterLat, 0)
}
return lng, lat, err
}