This commit is contained in:
richboo111
2023-05-23 09:38:57 +08:00
parent de25d3d9f7
commit e17074366b
8 changed files with 988 additions and 4 deletions

View File

@@ -5,6 +5,7 @@ const (
FnPsCode = "10004" // 蜂鸟配送
MTPsCode = "10032" // 美团配送
UUPTCode = "10006" //uu跑腿
SFPSCode = "10007" //顺丰配送
DYPsCode = "10018" //抖音配送(小时达)
MyselfPsCode = "10015" // 自送
)

View File

@@ -20,11 +20,11 @@ func init() {
baseapi.Init(sugarLogger)
// 菜市
//api = New("589", "a81eb3df418d83d6a1a4b7c572156d2f", "", "")
api = New("589", "a81eb3df418d83d6a1a4b7c572156d2f", "", "")
// 果园
// api = New("4123", "df2c88338b85f830cebce2a9eab56628", "", "")
api = New("4123", "df2c88338b85f830cebce2a9eab56628", "", "")
//api = New("4123", "df2c88338b85f830cebce2a9eab56628", "", "")
//商超
//api = New("5873", "41c479790a76f86326f89e8048964739", "", "token_n4TwqCntWWuvQwAawzxC0w") //token_n4TwqCntWWuvQwAawzxC0w

View File

@@ -59,12 +59,12 @@ type API struct {
/************************************************订单*****************************************************/
//#region 获取蜂鸟门店信息
// 创建订单
// CreateOrder 创建订单(店铺)
type CreateOrder struct {
// 必填
DevId int64 `json:"dev_id"` // 同城开发者ID
ShopId string `json:"shop_id"` // 店铺ID
ShopOrderId string `json:"shop_order_id"` // 商家订单号不允许重复
ShopOrderId string `json:"shop_order_id"` // 不允许重复(使用相同商家订单号会幂等返回)
OrderSource string `json:"order_source"` // 订单接入来源 1美团2饿了么3百度4口碑其他请直接填写中文字符串值
LbsType int `json:"lbs_type"` // 坐标类型 1百度坐标2高德坐标
PayType int64 `json:"pay_type"` // 用户支付方式 1已付款 0货到付款

View File

@@ -0,0 +1,141 @@
package sfps2
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
const (
// 回调url前缀
UrlIndexRiderStatus = "rider_status" //配送状态更改回调
UrlIndexRiderRecall = "rider_recall" //骑士撤单状态回调
UrlIndexOrderComplete = "order_complete" //订单完成回调
UrlIndexSFCancel = "sf_cancel" //顺丰原因订单取消回调
UrlIndexRiderException = "rider_exception" //订单异常回调
)
// RiderStatus 配送状态更改回调
type RiderStatus struct {
ShopId string `json:"shop_id"` // 店铺ID
SFOrderID string `json:"sf_order_id"` //顺丰订单ID
ShopOrderID string `json:"shop_order_id"` //商家订单ID
UrlIndex string `json:"url_index"` //回调url前缀
OperatorName string `json:"operator_name"` //配送员姓名
OperatorPhone string `json:"operator_phone"` //配送员电话
RiderLng string `json:"rider_lng"` //配送员位置经度
RiderLat string `json:"rider_lat"` //配送员位置纬度
OrderStatus int `json:"order_status"` //10-配送员接单/改派;12:配送员到店;15:配送员配送中
StatusDesc string `json:"status_desc"` //状态描述
PushTime int `json:"push_time"` //状态变更时间
}
// RiderRecall 骑士撤单状态回调
type RiderRecall struct {
ShopId string `json:"shop_id"` // 店铺ID
SFOrderID string `json:"sf_order_id"` //顺丰订单ID
ShopOrderID string `json:"shop_order_id"` //商家订单ID
UrlIndex string `json:"url_index"` //回调url前缀
OrderStatus int `json:"order_status"` //22-配送员撤单
StatusDesc string `json:"status_desc"` //状态描述
PushTime int `json:"push_time"` //状态变更时间
}
// OrderComplete 订单完成回调
type OrderComplete struct {
ShopId string `json:"shop_id"` // 店铺ID
SFOrderID string `json:"sf_order_id"` //顺丰订单ID
ShopOrderID string `json:"shop_order_id"` //商家订单ID
UrlIndex string `json:"url_index"` //回调url前缀
OperatorName string `json:"operator_name"` //配送员姓名
RiderLng string `json:"rider_lng"` //配送员位置经度
RiderLat string `json:"rider_lat"` //配送员位置纬度
OrderStatus int `json:"order_status"` //17配送员点击完成
StatusDesc string `json:"status_desc"` //状态描述
PickUpPic string `json:"pickup_pic"` //只有在店铺打开妥投照片回调开关且有妥投照片时才有此字段
PushTime int `json:"push_time"` //完成时间
ReceiptType int `json:"receipt_type"` //1:正常签收, 2:商家退回签收
}
// SFCancel 顺丰原因订单取消回调
type SFCancel struct {
ShopId string `json:"shop_id"` // 店铺ID
SFOrderID string `json:"sf_order_id"` //顺丰订单ID
ShopOrderID string `json:"shop_order_id"` //商家订单ID
UrlIndex string `json:"url_index"` //回调url前缀
OperatorName string `json:"operator_name"` //配送员姓名
OperatorPhone string `json:"operator_phone"` //配送员电话
OrderStatus int `json:"order_status"` //2:订单取消
StatusDesc string `json:"status_desc"` //状态描述
CancelReason string `json:"cancel_reason"` //取消原因
CancelCode string `json:"cancel_code"` //取消码
RiderLng string `json:"rider_lng"` //配送员位置经度
RiderLat string `json:"rider_lat"` //配送员位置纬度
PushTime int `json:"push_time"` //状态变更时间
}
// RiderException 配送状态更改回调
type RiderException struct {
ShopId string `json:"shop_id"` // 店铺ID
SFOrderID string `json:"sf_order_id"` //顺丰订单ID
ShopOrderID string `json:"shop_order_id"` //商家订单ID
UrlIndex string `json:"url_index"` //回调url前缀
OperatorName string `json:"operator_name"` //配送员姓名
OperatorPhone string `json:"operator_phone"` //配送员电话
OrderStatus int `json:"order_status"` //固定为91
StatusDesc string `json:"status_desc"` //状态描述
ExID int `json:"ex_id"` //异常ID
ExContent string `json:"ex_content"` //异常详情
ExpectTime string `json:"expect_time"` //新的预计送达时间
PushTime int `json:"push_time"` //顺丰推送时间
}
type CallbackResponse struct {
ErrorCode int `json:"error_code"`
ErrorMsg string `json:"error_msg"`
}
var (
SuccessResponse = &CallbackResponse{ErrorCode: SuccessCode, ErrorMsg: SuccessMsg}
)
func Err2CallbackResponse(err error) *CallbackResponse {
if err == nil {
return SuccessResponse
}
return &CallbackResponse{
ErrorCode: -1,
ErrorMsg: fmt.Sprint(err),
}
}
// GetRiderStatusCallback 配送状态更改回调
func (a *API) GetRiderStatusCallback(request *http.Request) (riderStatus *RiderStatus, response *CallbackResponse) {
data, err := ioutil.ReadAll(request.Body)
if err != nil {
response = &CallbackResponse{ErrorCode: -1}
return nil, response
}
if err = json.Unmarshal(data, &riderStatus); err != nil {
response = &CallbackResponse{ErrorCode: -1}
return nil, response
}
return riderStatus, SuccessResponse
}
// GetRiderExceptionCallback 订单异常回调
func (a *API) GetRiderExceptionCallback(request *http.Request) (riderException *RiderException, response *CallbackResponse) {
data, err := ioutil.ReadAll(request.Body)
if err != nil {
response = &CallbackResponse{ErrorCode: -1}
return nil, response
}
if err = json.Unmarshal(data, &riderException); err != nil {
response = &CallbackResponse{ErrorCode: -1}
return nil, response
}
return riderException, SuccessResponse
}

140
platformapi/sfps2/order.go Normal file
View File

@@ -0,0 +1,140 @@
package sfps2
import (
"encoding/json"
"errors"
"fmt"
"time"
)
// PreCreateOrder 预创建订单(店铺)
func (a *API) PreCreateOrder(preOrder *PreCreateOrderReq) (price float64, err error) {
//补全默认参数
preOrder.PushTime = time.Now().Unix()
preOrder.DevId = a.devId
resp := a.HttpPostJson("precreateorder", preOrder)
if resp.HttpStatusCode != HttpStatusSuccessCode {
return 0, errors.New("HTTP请求错误请检查重试")
}
if resp.BaseRetVal.ErrorCode != SuccessCode {
return 0, fmt.Errorf("%s", resp.BaseRetVal.ErrorMsg)
}
retVal := PreCreateOrderResp{}
s, _ := json.Marshal(resp.BaseRetVal.Result)
if err = json.Unmarshal(s, &retVal); err == nil {
return retVal.EstimatePayMoney, nil
} else {
return 0, err
}
}
// CreateOrder 创建订单(店铺)
func (a *API) CreateOrder(order *CreateOrderReq) (sfOrderID, sfBillID string, totalPrice int, err error) {
//补全默认参数
order.PushTime = time.Now().Unix()
order.Version = DefaultVersion
order.DevId = a.devId
resp := a.HttpPostJson("createorder", order)
if resp.HttpStatusCode != HttpStatusSuccessCode {
return "", "", 0, errors.New("HTTP请求错误请检查重试")
}
if resp.BaseRetVal.ErrorCode != SuccessCode {
return "", "", 0, fmt.Errorf("%s", resp.BaseRetVal.ErrorMsg)
}
retVal := CreateOrderResp{}
s, _ := json.Marshal(resp.BaseRetVal.Result)
if err = json.Unmarshal(s, &retVal); err == nil {
return retVal.SFOrderID, retVal.SFBillID, retVal.TotalPrice, nil
} else {
return "", "", 0, err
}
}
// PreCancelOrder 预取消订单
func (a *API) PreCancelOrder(sfOrderID string) (isCancel bool, err error) {
param := PreCancelOrderReq{
DevId: a.devId,
OrderID: sfOrderID,
PushTime: time.Now().Unix(),
}
resp := a.HttpPostJson("precancelorder", param)
if resp.HttpStatusCode != HttpStatusSuccessCode {
return false, errors.New("HTTP请求错误请检查重试")
}
if resp.BaseRetVal.ErrorCode != SuccessCode {
return false, fmt.Errorf("%s", resp.BaseRetVal.ErrorMsg)
}
retVal := PreCancelOrderResp{}
s, _ := json.Marshal(resp.BaseRetVal.Result)
if err = json.Unmarshal(s, &retVal); err == nil {
return retVal.CouldCancel, nil
} else {
return false, err
}
}
// CancelOrder 取消订单
func (a *API) CancelOrder(sfOrderID string) (err error) {
param := &CancelOrderReq{
DevId: a.devId,
OrderID: sfOrderID,
PushTime: time.Now().Unix(),
CancelCode: CancelCodeChangePlan,
}
resp := a.HttpPostJson("cancelorder", param)
if resp.HttpStatusCode != HttpStatusSuccessCode {
return errors.New("HTTP请求错误请检查重试")
}
if resp.BaseRetVal.ErrorCode != SuccessCode {
return fmt.Errorf("%s", resp.BaseRetVal.ErrorMsg)
}
return nil
}
// GetOrderStatus 订单实时信息查询
func (a *API) GetOrderStatus(sfOrderID string) (retVal *GetOrderStatusResp, err error) {
param := &GetOrderStatusReq{
DevId: a.devId,
OrderID: sfOrderID,
PushTime: time.Now().Unix(),
//OrderType: orderType,
}
resp := a.HttpPostJson("getorderstatus", param)
if resp.HttpStatusCode != HttpStatusSuccessCode {
return nil, errors.New("HTTP请求错误请检查重试")
}
if resp.BaseRetVal.ErrorCode != SuccessCode {
return nil, fmt.Errorf("%s", resp.BaseRetVal.ErrorMsg)
}
s, _ := json.Marshal(resp.BaseRetVal.Result)
if err = json.Unmarshal(s, &retVal); err == nil {
return retVal, nil
} else {
return nil, err
}
}
// GetRiderLatestPosition 获取骑手实时坐标
func (a *API) GetRiderLatestPosition(sfOrderID string) (retVal *RiderLatestPositionResp, err error) {
param := &RiderLatestPositionReq{
DevId: a.devId,
OrderID: sfOrderID,
PushTime: time.Now().Unix(),
OrderType: OrderTypeSF, //暂时默认
}
resp := a.HttpPostJson("riderlatestposition", param)
if resp.HttpStatusCode != HttpStatusSuccessCode {
return nil, errors.New("HTTP请求错误请检查重试")
}
if resp.BaseRetVal.ErrorCode != SuccessCode {
return nil, fmt.Errorf("%s", resp.BaseRetVal.ErrorMsg)
}
s, _ := json.Marshal(resp.BaseRetVal.Result)
if err = json.Unmarshal(s, &retVal); err == nil {
return retVal, nil
} else {
return nil, err
}
}

View File

@@ -0,0 +1,495 @@
package sfps2
import (
"git.rosy.net.cn/baseapi/platformapi"
"net/http"
"sync"
)
const (
BaseCatchUrl = "http://sfapi-proxy.jsonce.com" //抓包调试路由
BaseURL = "https://openic.sf-express.com/open/api/external" // 正式环境
RequestPost = "POST"
HttpStatusSuccessCode = 200 //http返回成功状态码
SuccessCode = 0 //成功code
SuccessMsg = "success" //成功 msg
FailCode = -1
FailMsg = "fail"
SFShopStoreID = "3243279847393" //默认以一个店铺发单
DefaultVersion = 19 //参照文档主版本号填写 如文档版本号1.9,version=19推荐使用版本19
)
const (
OrderTypeSF = 1 //1、顺丰订单号
OrderTypeStore = 2 //2、商家订单号
// 物品类型product_type枚举值
ProductTypeFastFood = 1 // 快餐
ProductTypeDrugs = 2 // 药品
ProductTypeDepartmentStore = 3 // 百货
ProductTypeOldClothes = 4 // 脏衣服收
ProductTypeNewClothes = 5 // 干净衣服派
ProductTypeFresh = 6 // 生鲜 目前默认
ProductTypeHighDrinks = 8 // 高端饮品
ProductTypeSiteInspection = 9 // 现场勘验
ProductTypeExpress = 10 // 快递
ProductTypeFile = 12 // 文件
ProductTypeCake = 13 // 蛋糕
ProductTypeFlower = 14 // 鲜花
ProductTypeDigital = 15 // 数码
ProductTypeClothing = 16 // 服装
ProductTypeCar = 17 // 汽配
ProductTypeJewellery = 18 // 珠宝
ProductTypePizza = 20 // 披萨
ProductTypeChineseFood = 21 // 中餐
ProductTypeFreshwaterFresh = 22 // 水产
ProductTypeDirectDelivery = 27 // 专人直送
ProductTypeMidRangeDrinks = 32 // 中端饮品
ProductTypeConvenienceStore = 33 // 便利店
ProductTypeBakeries = 34 // 面包糕点
ProductTypeHotPot = 35 // 火锅
ProductTypeLicence = 36 // 证照
ProductTypeCrayfish = 40 // 烧烤小龙虾
ProductTypeOtherInfo = 41 // 外部落地配
ProductTypeAlcoholAndTobacco = 47 // 烟酒行
ProductTypeAdultEroticaProducts = 48 // 成人用品
ProductTypeOther = 99 // 其他
//订单接入来源
OrderSourceMt = "1" //美团
OrderSourceELM = "2" //饿了么
OrderSourceEBAI = "3" //饿了么零售
OrderSourceKB = "4" //口碑
//坐标类型
LbsTypeBD = 1 //百度地图
LbsTypeGD = 2 //高德地图
//物流流向
RiderPickUpMethodSTU = 1 //从门店取件送至用户(默认)
RiderPickUpMethodUTS = 2 //从用户取件送至门店(仅连锁店铺支持)
//取消代码
CancelCodeChangePlan = 300 //计划有变,暂时不需要寄件了
CancelCodeWrongInfo = 302 //填错订单信息,取消后重新提交
CancelCodeRiderCancel = 303 //骑士要求取消
CancelCodeCanNotDelivery = 304 //暂时无法提供待配送物品
CancelCodeRepeatOrder = 306 //重复下单,取消此单
CancelCodeRiderTooLong = 309 //骑士上门时间太长
CancelCodeNoneTake = 312 //无人接单,换用其他平台寄件
CancelCodeOther = 313 //其他,请注明原因
//订单状态
OrderStatusNewOrder = 1 //1订单创建
OrderStatusTakeOrder = 10 //10配送员接单
OrderStatusArrivedStore = 12 //12配送员到店
OrderStatusRiderArriving = 15 //15配送员配送中已取货
OrderStatusRiderCancel = 22 //配送员撤单
OrderStatusFinished = 17 //17配送员完成订单
OrderStatusOrderCancel = 2 //订单取消
OrderStatusError = 91 //订单异常
//异常情况枚举
ExIDLoseOrBroke = 4003 //托寄物丢失或损坏
ExIDStoreSlow = 1001 //商家出货慢
ExIDUserRefuseCertifying = 2010 //顾客拒绝实名认证
ExIDCertifyingFail = 3004 //实名认证校验失败
ExIDChangeStoreAddress = 1007 //更改取货地址
ExIDUserDisconnect = 2001 //顾客电话无法接通
ExIDChangeExpectTime = 2004 //更改期望送达时间
ExIDUserReject = 2005 //顾客拒收
ExIDUserNotHome = 2008 //顾客不在家
ExIDChangeUserAddress = 2009 //更改送货地址
ExIDWrongAddress = 4001 //配送地址错误
ExIDOther = 4002 //其他
)
var CancelCode = map[int]string{
CancelCodeChangePlan: "计划有变,暂时不需要寄件了",
CancelCodeWrongInfo: "填错订单信息,取消后重新提交",
CancelCodeRiderCancel: "骑士要求取消",
CancelCodeCanNotDelivery: "暂时无法提供待配送物品",
CancelCodeRepeatOrder: "重复下单,取消此单",
CancelCodeRiderTooLong: "骑士上门时间太长",
CancelCodeNoneTake: "无人接单,换用其他平台寄件",
CancelCodeOther: "其他,请注明原因",
}
// API 注册请求api
type API struct {
devId int64 `json:"dev_id"`
devKey string `json:"dev_key"`
sign string `json:"sign"`
pushTime int64 `json:"push_time"`
locker sync.RWMutex
client *http.Client
config *platformapi.APIConfig
}
/************************************************订单*****************************************************/
// PreCreateOrderReq 预创建订单(店铺)请求
type PreCreateOrderReq struct {
// 必填
DevId int64 `json:"dev_id"` // 同城开发者ID
ShopId string `json:"shop_id"` // 店铺ID
UserLng string `json:"user_lng"` //用户地址经度
UserLat string `json:"user_lat"` //用户地址纬度
UserAddress string `json:"user_address"` //用户详细地址
Weight int64 `json:"weight"` //物品重量(单位:克)
ProductType int64 `json:"product_type"` //物品类型
PushTime int64 `json:"push_time"` // 推单时间 秒级时间戳
// 非必填
ShopType int64 `json:"shop_type"` // 店铺ID类型 1顺丰店铺ID 2接入方店铺ID
CityName string `json:"city_name"` //发单城市 用来校验是否跨城;请填写城市的中文名称,如北京市、深圳市
TotalPrice int64 `json:"total_price"` //用户订单总金额(单位:分)
IsAppoint int `json:"is_appoint"` //是否是预约单 0非预约单1预约单
AppointType int `json:"appoint_type"` //预约单类型 预约单的时候传入1预约单送达单2预约单上门单
ExpectTime int64 `json:"expect_time"` // 用户期望送达时间 若传入自此段且时间大于配送时效则按照预约送达单处理时间小于配送时效按照立即单处理appoint_type=1时需必传,秒级时间戳;
//ExpectPickupTime int64 `json:"expect_pickup_time"` // 用户期望上门时间 appoint_type=2时需必传,秒级时间戳
LbsType int `json:"lbs_type"` // 坐标类型 1百度坐标2高德坐标
IsInsured int64 `json:"is_insured"` // 是否保价0非保价1保价
IsPersonDirect int64 `json:"is_person_direct"` // 是否是专人直送订单01
Vehicle int `json:"vehicle"` // 配送交通工具01电动车2小轿车
DeclaredValue int64 `json:"declared_value"` // 保价金额(单位:分)
GratuityFee int64 `json:"gratuity_fee"` // 订单小费不传或者传0为不加小费 单位分加小费最低不能少于100分
RiderPickMethod int64 `json:"rider_pick_method"` // 物流流向 1从门店取件送至用户 2从用户取件送至门店
ReturnFlag int `json:"return_flag"` // 返回字段控制标志位(二进制) 1:商品总价格2:配送距离4:物品重量8:起送时间16:期望送达时间32:支付费用64:实际支付金额128:优惠券总金额256:结算方式 例如全部返回为填入511
Shop *SfShopInfo `json:"shop"` // 发货店铺信息 Obj详见shop结构 平台级开发者(如饿了么)需传入如无特殊说明此字段可忽略
MultiPickupInfo []*MultiPickupInfo `json:"multi_pickup_info"` // 多点取货信息
}
// PreCreateOrderResp 预创建订单(店铺)响应
type PreCreateOrderResp struct {
ChargePriceList *ChargePriceList `json:"charge_price_list"`
DeliveryType float64 `json:"delivery_type"` //0预约送达单 1立即单 3预约上门单
EstimateCouponDetail []interface{} `json:"estimate_coupon_detail"`
EstimateCouponTotalFee float64 `json:"estimate_coupon_total_fee"`
EstimatePayMoney float64 `json:"estimate_pay_money"`
ExpectTime float64 `json:"expect_time"`
OrderToken string `json:"order_token"`
OverflowExpectTime float64 `json:"overflow_expect_time"`
OverflowFee float64 `json:"overflow_fee"`
PromiseDeliveryTime float64 `json:"promise_delivery_time"` //预计配送时间(单位: 分)
GratuityFee float64 `json:"gratuity_fee"` //订单小费
PushTime float64 `json:"push_time"` //时间戳
}
type ChargePriceList struct {
ChargeDetail *ChargeDetail `json:"charge_detail"`
InsuredCeilingRule *InsuredCeilingRule `json:"insured_ceiling_rule"`
ShopPayPrice float64 `json:"shop_pay_price"` //配送费总额(单位:分)
IsInsuredRule float64 `json:"is_insured_rule"`
ShopDeductionPrice float64 `json:"shop_deduction_price"`
Vehicle float64 `json:"vehicle"`
}
type ChargeDetail struct {
BasicFee float64 `json:"basic_fee"` //常规配送费=起步价+超距离费+超重量费
Basic float64 `json:"basic"` //起步价
CancelExcessFee float64 `json:"cancel_excess_fee"` //拒收扣费
ExtraFee float64 `json:"extra_fee"` //附加费
ExtraFeeDetail struct {
GeographyFee float64 `json:"geography_fee"`
}
GratuityFee float64 `json:"gratuity_fee"`
OverDistance float64 `json:"over_distance"` //超距离费用
OverWeight float64 `json:"over_weight"` //超重量费用
OverflowFee float64 `json:"overflow_fee"`
SpecialTimeFee float64 `json:"special_time_fee"` //特殊时段费
VasFee float64 `json:"vas_fee"` //增值服务费
VasFeeDetail *VasFeeDetail `json:"vas_fee_detail"` //增值服务费详情
}
type InsuredCeilingRule struct {
End float64 `json:"end"`
InsuredType float64 `json:"insured_type"`
Start float64 `json:"start"`
Status float64 `json:"status"`
Type float64 `json:"type"`
Value string `json:"value"`
}
// VasFeeDetail 增值服务费详情
type VasFeeDetail struct {
AcceptFasterFee float64 `json:"accept_faster_fee"`
AddressChangeFee float64 `json:"address_change_fee"`
AppointmentFasterFee float64 `json:"appointment_faster_fee"`
AppointmentFee float64 `json:"appointment_fee"`
BigOrder struct {
Fee float64 `json:"fee"` //大额单费
} `json:"big_order"`
Collection struct {
Fee float64 `json:"fee"` //代收货款费用
} `json:"collection"`
Countersign struct {
Fee float64 `json:"fee"`
} `json:"countersign"`
DigitalServiceFee float64 `json:"digital_service_fee"`
DwPackageFee float64 `json:"dw_package_fee"`
ExpectFasterFee float64 `json:"expect_faster_fee"`
FileBagFee float64 `json:"file_bag_fee"`
Insured struct {
DeclaredPrice float64 `json:"declared_price"`
Fee float64 `json:"fee"`
} `json:"insured"`
LineupTimeFee float64 `json:"lineup_time_fee"`
LowTempFee float64 `json:"low_temp_fee"`
MultiPickup struct {
Fee int `json:"fee"`
} `json:"multipickup"`
OutOrderRateFee float64 `json:"out_order_rate_fee"`
PackageServiceFee float64 `json:"package_service_fee"`
PackingFee float64 `json:"packing_fee"`
PersonDirectFee float64 `json:"person_direct_fee"`
ResidentServiceFee float64 `json:"resident_service_fee"`
ScanPayFee float64 `json:"scan_pay_fee"`
ServiceFee float64 `json:"service_fee"`
TakeGoodsSmsFee float64 `json:"take_goods_sms_fee"`
UavFee float64 `json:"uav_fee"`
VehicleCarFee float64 `json:"vehicle_car_fee"`
}
// CreateOrderReq 创建订单(店铺)
type CreateOrderReq struct {
// 必填
DevId int64 `json:"dev_id"` // 同城开发者ID
ShopId string `json:"shop_id"` // 店铺ID
ShopOrderId string `json:"shop_order_id"` // 不允许重复(使用相同商家订单号会幂等返回)
OrderSource string `json:"order_source"` // 订单接入来源 1美团2饿了么3百度4口碑其他请直接填写中文字符串值
PayType int64 `json:"pay_type"` // 用户支付方式 1已付款 0货到付款
OrderTime int64 `json:"order_time"` // 用户下单时间 秒级时间戳
IsAppoint int `json:"is_appoint"` // 是否是预约单 0非预约单1预约单
IsInsured int64 `json:"is_insured"` // 是否保价0非保价1保价
IsPriorityAssign int64 `json:"is_priority_assign"` // 是否优先派单0否 1
IsPersonDirect int64 `json:"is_person_direct"` // 是否是专人直送订单01
PushTime int64 `json:"push_time"` // 推单时间 秒级时间戳
Version int64 `json:"version"` // 版本号 参照文档主版本号填写 如文档版本号1.7,version=17
Receive *ReceiveAddress `json:"receive"` // 收货人信息
Shop *SfShopInfo `json:"shop"` // 发货店铺信息 Obj详见shop结构 平台级开发者(如饿了么)需传入如无特殊说明此字段可忽略
OrderDetail *OrderDetail `json:"order_detail"` // 订单详情
MultiPickupInfo []*MultiPickupDetails `json:"multi_pickup_info"` // 多点取货信息
// 非必填
//LbsType int `json:"lbs_type"` // 坐标类型 1百度坐标2高德坐标
//ShopType int64 `json:"shop_type"` // 店铺ID类型 1顺丰店铺ID 2接入方店铺ID
ShopPreparationTime int64 `json:"shop_preparation_time"` // 商家预计备餐时长(分10)
OrderSequence string `json:"order_sequence"` // 取货序号 与order_source配合使用 如饿了么10号单表示如下order_source=2;order_sequence=10。用于骑士快速寻找配送物
AppointType int `json:"appoint_type"` // 预约单类型 预约单的时候传入,1预约单送达单2预约单上门单
ExpectTime int64 `json:"expect_time"` // 用户期望送达时间 若传入自此段且时间大于配送时效则按照预约送达单处理时间小于配送时效按照立即单处理appoint_type=1时需必传,秒级时间戳;
ExpectPickupTime int64 `json:"expect_pickup_time"` // 用户期望上门时间 appoint_type=2时需必传,秒级时间戳
ShopExpectTime int64 `json:"shop_expect_time"` // 商家期望送达时间 只展示给骑士,不参与时效考核;秒级时间戳
Vehicle int `json:"vehicle"` // 配送交通工具01电动车2小轿车
DeclaredValue int64 `json:"declared_value"` // 保价金额(单位:分)
GratuityFee int64 `json:"gratuity_fee"` // 订单小费不传或者传0为不加小费 单位分加小费最低不能少于100分
Remark string `json:"remark"` // 订单备注
RiderPickMethod int64 `json:"rider_pick_method"` // 物流流向 1从门店取件送至用户 2从用户取件送至门店
ReturnFlag int `json:"return_flag"` // 返回字段控制标志位(二进制) 1:商品总价格2:配送距离4:物品重量8:起送时间16:期望送达时间32:支付费用64:实际支付金额128:优惠券总金额256:结算方式 例如全部返回为填入511
}
// CreateOrderResp 创建订单(店铺)
type CreateOrderResp struct {
SFOrderID string `json:"sf_order_id"` //顺丰订单号, 请注意新版本V1.9及以后该订单号升级为JS开头的15位订单号类型为字符串老版本仍为int类型
SFBillID string `json:"sf_bill_id"` //顺丰运单号(需要在顺丰后台配置门店后返回此字段)
ShopOrderID string `json:"shop_order_id"` //商家订单号
PushTime int `json:"push_time"` //推送时间
//以下字段受请求参数中 return_flag 控制return_flag中未包含的此字段将不存在请注意
TotalPrice int `json:"total_price"` //配送费总额当return_flag中包含1时返回单位分值为计算出来此单总价
DeliveryDistanceMeter int `json:"delivery_distance_meter"` //配送距离当return_flag中包含2时返回单位米值为计算出来实际配送距离
WeightGram int `json:"weight_gram"` //商品重量当return_flag中包含4时返回单位克值为下单传入参数回传
StartTime int `json:"start_time"` //起送时间当return_flag中包含8时返回时间格式为Unix时间戳注意转换
ExpectTime int `json:"expect_time"` //预计送达时间当return_flag中包含16时返回时间格式为Unix时间戳注意转换
TotalPayMoney int `json:"total_pay_money"` //支付费用当return_flag中包含32时返回单位分
RealPayMoney int `json:"real_pay_money"` //实际支付金额当return_flag中包含64时返回单位分实际支付金额=总金额-优惠券总金额)
CouponsTotalFee int `json:"coupons_total_fee"` //优惠券总金额当return_flag中包含128时返回单位分
SettlementType int `json:"settlement_type"` //结算方式当return_flag中包含256时返回
PickUPCode int `json:"pick_up_code"` //取件码。在顺丰同城商户侧配置,配置后有此字段
CompleteCode int `json:"complete_code"` //签收码。在顺丰同城商户侧配置,配置后有此字段
OverflowFee int `json:"overflow_fee"` //爆单费,单位分
InsureFee int `json:"insure_fee"` //保价费,单位分
}
// PreCancelOrderReq 预取消订单
type PreCancelOrderReq struct {
//必填
DevId int64 `json:"dev_id"` // 同城开发者ID
OrderID string `json:"order_id"` //新版本V1.9+升级为JS开头的15位字符串类型:“JS1234567890123”, 老版本int类型订单号会长期兼容
PushTime int64 `json:"push_time"` //取消时间;秒级时间戳
//非必填
ShopId string `json:"shop_id"` // 店铺ID
ShopType int64 `json:"shop_type"` //1、顺丰店铺ID 2、接入方店铺ID
CancelReason string `json:"cancel_reason"` //其他取消原因
}
// PreCancelOrderResp 预取消订单
type PreCancelOrderResp struct {
OrderType float64 `json:"order_type"`
PromiseDeliveryTime float64 `json:"promise_delivery_time"` //预计配送时间(单位: 分)
DeliveryType float64 `json:"delivery_type"` //订单类型 0预约送达单1立即单2预约上门单
ExpectPickUpTime float64 `json:"expect_pickup_time"` //原始期望上门时间
ExpectTime float64 `json:"expect_time"` //预约时间
ShopCancelTimes string `json:"shop_cancel_times"` //店铺每日取消次数
FreeCancelTimes float64 `json:"free_cancel_times"` //每日免费取消次数
IsCancelChargePriceRule float64 `json:"is_cancel_charge_price_rule"` //取消收费规则
IsOverFreeCancelTimes float64 `json:"is_over_free_cancel_times"` //是否超出免费取消次数
IsDeductionFee float64 `json:"is_deduction_fee"` //是否有取消收费
DeductionFee float64 `json:"deduction_fee"` //取消收费
//CancelChargePriceRule
CouldCancel bool `json:"could_cancel"` //能否取消
PushTime float64 `json:"push_time"` //推单时间
SFOrderID string `json:"sf_order_id"` //顺丰订单号, 请注意新版本V1.9及以后该订单号升级为JS开头的15位订单号类型为字符串老版本仍为int类型
ShopOrderID string `json:"shop_order_id"` //商家订单号
}
// CancelOrderReq 取消订单
type CancelOrderReq struct {
//必填
DevId int64 `json:"dev_id"` // 同城开发者ID
OrderID string `json:"order_id"` //新版本V1.9+升级为JS开头的15位字符串类型:“JS1234567890123”, 老版本int类型订单号会长期兼容
PushTime int64 `json:"push_time"` //取消时间;秒级时间戳
//非必填
OrderType int64 `json:"order_type"` //1、顺丰订单号 2、商家订单号
ShopId string `json:"shop_id"` // 店铺ID
ShopType int64 `json:"shop_type"` //1、顺丰店铺ID 2、接入方店铺ID
CancelCode int64 `json:"cancel_code"` //不填时默认cancel_code=313,cancel_reason=商家发起取消
CancelReason string `json:"cancel_reason"` //其他取消原因
}
// CancelOrderResp 取消订单
type CancelOrderResp struct {
SFOrderID string `json:"sf_order_id"` //顺丰订单号, 请注意新版本V1.9及以后该订单号升级为JS开头的15位订单号类型为字符串老版本仍为int类型
ShopOrderID string `json:"shop_order_id"` //商家订单号
DeductionDetails struct {
DeductionFee int `json:"deduction_fee"` //取消收费金额(单位:分)
ShopCancelTimes int `json:"shop_cancel_times"` //店铺维度累计的取消次数
FreeCancelTimes int `json:"free_cancel_times"` //配置的免费取消次数
}
PushTime int `json:"push_time"` //接口返回时间
}
// GetOrderStatusReq 订单实时信息查询
type GetOrderStatusReq struct {
//必填
DevId int64 `json:"dev_id"` // 同城开发者ID
OrderID string `json:"order_id"` //新版本V1.9+升级为JS开头的15位字符串类型:“JS1234567890123”, 老版本int类型订单号会长期兼容
PushTime int64 `json:"push_time"` //取消时间;秒级时间戳
//非必填
OrderType int64 `json:"order_type"` //1、顺丰订单号 2、商家订单号
ShopId string `json:"shop_id"` // 店铺ID
ShopType int64 `json:"shop_type"` //1、顺丰店铺ID 2、接入方店铺ID
}
// GetOrderStatusResp 订单实时信息查询
type GetOrderStatusResp struct {
OrderID string `json:"order_id"` //新版本V1.9+升级为JS开头的15位字符串类型:“JS1234567890123”, 老版本int类型订单号会长期兼容
ShopId int64 `json:"shop_id"` // 店铺ID
OutOrderID string `json:"out_order_id"` //商家订单ID
OrderStatus float64 `json:"order_status"` //当前状态
StatusDesc string `json:"status_desc"` //当前状态描述
RiderName string `json:"rider_name"` //骑士名称
RiderPhone string `json:"rider_phone"` //骑手电话
PushTime int64 `json:"push_time"` //订单推送生成的时间
TotalPrice float64 `json:"total_price"` //配送费总额
DeliveryDistanceMeter float64 `json:"delivery_distance_meter"` //配送距离
WeightGram float64 `json:"weight_gram"` //商品重量
StartTime int64 `json:"start_time"` //起送时间
ExpectTime int64 `json:"expect_time"` //预计送达时间
TotalPayMoney float64 `json:"total_pay_money"` //支付费用
RealPayMoney float64 `json:"real_pay_money"` //实际支付金额
CouponsTotalFee float64 `json:"coupons_total_fee"` //优惠券总金额
SettlementType float64 `json:"settlement_type"` //结算方式
PickUpCode float64 `json:"pick_up_code"` //取件码
CompleteCode float64 `json:"complete_code"` //签收码
OverflowFee float64 `json:"overflow_fee"` //爆单费,单位分
}
// RiderLatestPositionReq 获取配送员实时坐标接口
type RiderLatestPositionReq struct {
DevId int64 `json:"dev_id"` // 同城开发者ID
OrderID string `json:"order_id"` //新版本V1.9+升级为JS开头的15位字符串类型:“JS1234567890123”, 老版本int类型订单号会长期兼容
PushTime int64 `json:"push_time"`
//非必填
OrderType int64 `json:"order_type"` //查询订单ID类型1、顺丰订单号 2、商家订单号
ShopID int64 `json:"shop_id"` //order_type=2时必传shop_id与shop_type
ShopType int64 `json:"shop_type"` //1、顺丰店铺ID 2、接入方店铺ID
}
// RiderLatestPositionResp 获取配送员实时坐标接口
type RiderLatestPositionResp struct {
SFOrderID string `json:"sf_order_id"` //顺丰订单号新版本V1.9+升级为JS开头的15位字符串类型, 老版本为int类型
ShopOrderID string `json:"shop_order_id"` //商家订单号
RiderName string `json:"rider_name"` //配送员姓名
RiderPhone string `json:"rider_phone"` //配送员联系方式
RiderLng string `json:"rider_lng"` //配送员经度
RiderLat string `json:"rider_lat"` //配送员纬度
UploadTime string `json:"upload_time"` //坐标上传时间(秒级时间戳)
}
// SfShopInfo 发货店铺信息,必填
type SfShopInfo struct {
ShopName string `json:"shop_name"` // 店铺名称
ShopPhone string `json:"shop_phone"` // 店铺电话
ShopAddress string `json:"shop_address"` // 店铺地址
ShopLng string `json:"shop_lng"` // 店铺经度
ShopLat string `json:"shop_lat"` // 店铺纬度
}
// ReceiveAddress 收货人信息
type ReceiveAddress struct {
// 必填
UserName string `json:"user_name"` // 用户姓名
UserPhone string `json:"user_phone"` // 用户电话
UserAddress string `json:"user_address"` // 用户地址
UserLng string `json:"user_lng"` // 用户经度
UserLat string `json:"user_lat"` // 用户纬度
// 非必填
CityName string `json:"city_name"` // 发单城市
}
// OrderDetail 订单详情
type OrderDetail struct {
// 必填
TotalPrice int64 `json:"total_price"` // 订单金额 分
ProductType int64 `json:"product_type"` // 物品类型 枚举值见下面定义
WeightGram int64 `json:"weight_gram"` // 物品重量(单位:克)
ProductNum int64 `json:"product_num"` // 物品个数
ProductTypeNum int64 `json:"product_type_num"` // 物品种类个数
ProductDetail []*ProductDetail `json:"product_detail"` // 物品详情数组结构详见product_detail结构
// 非必填
UserMoney int64 `json:"user_money"` // 用户实付商家金额(单位:分)
ShopMoney int `json:"shop_money"` // 商家实收用户金额(单位:分)
VolumeLitre int64 `json:"volume_litre"` // 物品体积(单位:升)
DeliveryMoney int64 `json:"delivery_money"` // 商家收取用户的配送费(单位:分)
}
// ProductDetail 购买物品详情清单
type ProductDetail struct {
// 必填
ProductName string `json:"product_name"` // 物品名称
ProductNum int64 `json:"product_num"` // 物品数量
// 非必填
ProductId int64 `json:"product_id"` // 物品ID
ProductPrice int64 `json:"product_price"` // 物品价格
ProductUnit string `json:"product_unit"` // 物品单位
ProductRemark string `json:"product_remark"` // 备注
ItemDetail string `json:"item_detail"` // 详情
}
// MultiPickupDetails 取货地址详情
type MultiPickupDetails struct {
PickupShopAddress string `json:"pickup_shop_address"` // 取货点地址
PickupShopPhone string `json:"pickup_shop_phone"` // 取货点店铺手机号
PickupShopName string `json:"pickup_shop_name"` // 取货点店铺名称
PickupLng string `json:"pickup_lng"` // 取货点经度
PickupLat string `json:"pickup_lat"` // 取货点纬度
PickupProducts string `json:"pickup_products"` // 取货点店铺物品信息
}
// MultiPickupInfo 多点取货信息
type MultiPickupInfo struct {
PickupShopAddress string `json:"pickup_shop_address"` // 取货点地址
PickupLng string `json:"pickup_lng"` // 取货点经度
PickupLat string `json:"pickup_lat"` // 取货点纬度
}
//#endregion

View File

@@ -0,0 +1,109 @@
package sfps2
import (
"fmt"
"git.rosy.net.cn/baseapi/utils"
"testing"
)
var api = New(AppID, AppKey)
const (
AppID = 1663705378 //开发者ID
AppKey = "0838426b310fd2530c57dd6e770ddff1" //开发者密钥
TestSFStoreID = "3243279847393" //open测试平台型店铺
)
//预下单
func TestPreCreateOrder(t *testing.T) {
param := &PreCreateOrderReq{
DevId: AppID,
ShopId: TestSFStoreID,
UserLng: "116.339392",
UserLat: "40.002349",
UserAddress: "北京市海淀区学清嘉创大厦A座15层",
Weight: 1000,
ProductType: 4,
PushTime: 1684379930,
ShopType: 1,
LbsType: LbsTypeGD,
RiderPickMethod: 1,
ReturnFlag: 1,
//IsAppoint: 0,
//AppointType: 2,
//ExpectPickupTime: int64(time.Now().Unix()),
//RiderPickMethod: 1,
//MultiPickupInfo: []*MultiPickupInfo{{
// PickupShopAddress: "海淀区清河龙岗路51号清润家园小区 永辉",
// PickupLat: "40.030613",
// PickupLng: "116.354787",
//}},
}
resp, err := api.PreCreateOrder(param)
fmt.Println(resp)
fmt.Println(err)
}
//正式下单
func TestCreateOrder(t *testing.T) {
param := &CreateOrderReq{
DevId: AppID,
ShopId: TestSFStoreID,
ShopOrderId: "20230518Test",
OrderSequence: "测试",
OrderSource: OrderSourceELM,
OrderTime: 1684302448,
PushTime: 1684399264,
Version: 19,
Receive: &ReceiveAddress{
UserLng: "116.339392",
UserLat: "40.002349",
UserAddress: "北京市海淀区学清嘉创大厦A座15层",
UserName: "杨玺",
UserPhone: "17236456352",
},
OrderDetail: &OrderDetail{
TotalPrice: 890,
ProductType: 4,
WeightGram: 390,
ProductNum: 1,
ProductTypeNum: 1,
ProductDetail: []*ProductDetail{{
ProductName: "新鲜水果拼盘",
ProductNum: 1,
}},
},
RiderPickMethod: 1,
}
sfOrderID, sfBillID, totalPrice, err := api.CreateOrder(param)
fmt.Println(sfOrderID, sfBillID)
fmt.Println(totalPrice)
fmt.Println(err)
}
//预取消订单
func TestPreCancelOrder(t *testing.T) {
resp, err := api.PreCancelOrder("JS4157196256886")
fmt.Println(resp)
fmt.Println(err)
}
//取消订单
func TestCancelOrder(t *testing.T) {
err := api.CancelOrder("JS4157196256886")
fmt.Println(err)
}
//订单实时信息查询
func TestGetOrderStatus(t *testing.T) {
resp, err := api.GetOrderStatus("JS4157196256886")
fmt.Println(utils.Format4Output(resp, false))
fmt.Println(err)
}
//
func TestGetRiderLatestPosition(t *testing.T) {
resp, err := api.GetRiderLatestPosition("JS4157196256886")
fmt.Println(utils.Format4Output(resp, false))
fmt.Println(err)
}

View File

@@ -0,0 +1,98 @@
package sfps2
import (
"crypto/md5"
"encoding/base64"
"encoding/json"
"fmt"
"git.rosy.net.cn/baseapi/platformapi"
"git.rosy.net.cn/baseapi/utils"
"io/ioutil"
"net/http"
"strings"
"sync"
)
type Response struct {
HttpStatusCode int `json:"http_status_code"`
BaseRetVal *BaseRetVal
}
type BaseRetVal struct {
ErrorCode int `json:"error_code"`
ErrorMsg string `json:"error_msg"`
ErrorData interface{} `json:"error_data"`
Result interface{} `json:"result"`
}
func New(devId int64, devKey string, config ...*platformapi.APIConfig) *API {
curConfig := platformapi.DefAPIConfig
if len(config) > 0 {
curConfig = *config[0]
}
return &API{
devId: devId,
devKey: devKey,
locker: sync.RWMutex{},
client: &http.Client{Timeout: curConfig.ClientTimeout},
config: &curConfig,
}
}
func (a *API) signParam(params []byte) (sig string) {
sign := fmt.Sprintf("%s&%d&%s", string(params), a.devId, a.devKey)
md2sign := md5.Sum([]byte(sign))
return base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%x", md2sign)))
}
func (a *API) HttpPostJson(url string, data interface{}) *Response {
//序列化参数
b, err := json.Marshal(data)
if err != nil {
var msg = fmt.Sprintf("json serialize err:%+v", err)
fmt.Println(msg)
result := Response{
HttpStatusCode: 500,
}
return &result
}
//签名
sign := a.signParam(b)
fullUrl := utils.GenerateGetURL(BaseURL, url, map[string]interface{}{"sign": sign})
//fullUrl := utils.GenerateGetURL(BaseCatchUrl, url, map[string]interface{}{"sign": sign})
request, err := http.NewRequest(http.MethodPost, fullUrl, strings.NewReader(string(b)))
client := &http.Client{}
request.Header.Set("Content-Type", "application/json;charset=UTF-8")
//request.Header.Set("Test-Group", "jx517")
resp, err := client.Do(request)
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
var msg = fmt.Sprintf("post json error:%+v", err)
fmt.Println(msg)
}
result := Response{
HttpStatusCode: resp.StatusCode,
}
var content BaseRetVal
err = json.Unmarshal(body, &content)
if err == nil {
result.BaseRetVal = &content
} else {
var msg = fmt.Sprintf("unmarshal body failed, error:%+v", err)
fmt.Println(msg)
}
return &result
}