- big big refactor.
This commit is contained in:
81
platformapi/dadaapi/callback.go
Normal file
81
platformapi/dadaapi/callback.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package dadaapi
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/structs"
|
||||
|
||||
"git.rosy.net.cn/baseapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
ResponseHttpCodeSuccess = 200
|
||||
ResponseHttpCodeGeneralErr = 555
|
||||
)
|
||||
|
||||
type CallbackMsg struct {
|
||||
ClientID string `json:"client_id"`
|
||||
OrderID string `json:"order_id"`
|
||||
OrderStatus int `json:"order_status"`
|
||||
CancelReason string `json:"cancel_reason"`
|
||||
CancelFrom int `json:"cancel_from"`
|
||||
UpdateTime int `json:"update_time"`
|
||||
Signature string `json:"signature"`
|
||||
DmID int `json:"dm_id"`
|
||||
DmName string `json:"dm_name"`
|
||||
DmMobile string `json:"dm_mobile"`
|
||||
}
|
||||
|
||||
type CallbackResponse struct {
|
||||
Code int
|
||||
Dummy string `json:"dummy"`
|
||||
}
|
||||
|
||||
var (
|
||||
SuccessResponse = &CallbackResponse{Code: ResponseHttpCodeSuccess}
|
||||
FailedResponse = &CallbackResponse{Code: ResponseHttpCodeGeneralErr}
|
||||
)
|
||||
|
||||
func (a *API) signCallbackParams(mapData map[string]interface{}) string {
|
||||
values := make([]string, 0)
|
||||
for _, k := range []string{"client_id", "order_id", "update_time"} {
|
||||
values = append(values, fmt.Sprint(mapData[k]))
|
||||
}
|
||||
sort.Strings(values)
|
||||
|
||||
finalStr := strings.Join(values, "")
|
||||
// baseapi.SugarLogger.Debugf("sign str:%v", finalStr)
|
||||
return fmt.Sprintf("%x", md5.Sum([]byte(finalStr)))
|
||||
}
|
||||
|
||||
func (a *API) unmarshalData(data []byte, msg interface{}) (callbackResponse *CallbackResponse) {
|
||||
err := utils.UnmarshalUseNumber(data, msg)
|
||||
if err != nil {
|
||||
return FailedResponse
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *API) CheckCallbackValidation(mapData map[string]interface{}) (callbackResponse *CallbackResponse) {
|
||||
sign := a.signCallbackParams(mapData)
|
||||
if remoteSign, _ := mapData[signKey].(string); sign != remoteSign {
|
||||
baseapi.SugarLogger.Infof("Signature is not ok, mine:%v, get:%v", sign, remoteSign)
|
||||
return FailedResponse
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *API) GetOrderCallbackMsg(data []byte) (msg *CallbackMsg, callbackResponse *CallbackResponse) {
|
||||
msg = new(CallbackMsg)
|
||||
if callbackResponse = a.unmarshalData(data, msg); callbackResponse != nil {
|
||||
return nil, FailedResponse
|
||||
}
|
||||
|
||||
mapData := structs.Map(msg)
|
||||
callbackResponse = a.CheckCallbackValidation(mapData)
|
||||
return msg, callbackResponse
|
||||
}
|
||||
176
platformapi/dadaapi/dadaapi.go
Normal file
176
platformapi/dadaapi/dadaapi.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package dadaapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"git.rosy.net.cn/baseapi"
|
||||
"git.rosy.net.cn/baseapi/platformapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
sandboxURL = "http://newopen.qa.imdada.cn/"
|
||||
prodURL = "http://newopen.imdada.cn/"
|
||||
signKey = "signature"
|
||||
)
|
||||
|
||||
const (
|
||||
ResponseCodeException = -1
|
||||
ResponseCodeSuccess = 0
|
||||
ResponseCodeSignErr = 2003
|
||||
ResponseCodeRetryLater = 2012
|
||||
ResponseCodeNetworkErr = 2455
|
||||
)
|
||||
|
||||
type API struct {
|
||||
appKey string
|
||||
appSecret string
|
||||
sourceID string
|
||||
url string
|
||||
callbackURL string
|
||||
client *http.Client
|
||||
config *platformapi.APIConfig
|
||||
}
|
||||
|
||||
type ResponseResult struct {
|
||||
Status string `json:"status"`
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Result interface{} `json:"result"`
|
||||
ErrorCode int `json:"errorCode"`
|
||||
}
|
||||
|
||||
type City struct {
|
||||
CityName string `json:"cityName"`
|
||||
CityCode string `json:"cityCode"`
|
||||
}
|
||||
|
||||
type CancelReason struct {
|
||||
ID int `json:"id"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
func New(appKey, appSecret, sourceId, callbackURL string, isProd bool, config ...*platformapi.APIConfig) *API {
|
||||
curConfig := platformapi.DefAPIConfig
|
||||
if len(config) > 0 {
|
||||
curConfig = *config[0]
|
||||
}
|
||||
api := &API{
|
||||
appKey: appKey,
|
||||
appSecret: appSecret,
|
||||
sourceID: sourceId,
|
||||
callbackURL: callbackURL,
|
||||
client: &http.Client{Timeout: curConfig.ClientTimeout},
|
||||
config: &curConfig,
|
||||
}
|
||||
if isProd {
|
||||
api.url = prodURL
|
||||
} else {
|
||||
api.url = sandboxURL
|
||||
}
|
||||
return api
|
||||
}
|
||||
|
||||
func (a *API) signParams(mapData map[string]interface{}) string {
|
||||
keys := make([]string, 0)
|
||||
for k := range mapData {
|
||||
if k != signKey {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
finalStr := a.appSecret
|
||||
for _, k := range keys {
|
||||
finalStr += k + fmt.Sprint(mapData[k])
|
||||
}
|
||||
|
||||
finalStr += a.appSecret
|
||||
// baseapi.SugarLogger.Debugf("sign str:%v", finalStr)
|
||||
return fmt.Sprintf("%X", md5.Sum([]byte(finalStr)))
|
||||
}
|
||||
|
||||
func (a *API) AccessAPI(action string, params map[string]interface{}) (retVal *ResponseResult, err error) {
|
||||
params2 := make(map[string]interface{})
|
||||
|
||||
params2["app_key"] = a.appKey
|
||||
params2["timestamp"] = utils.Int64ToStr(utils.GetCurTimestamp())
|
||||
params2["format"] = "json"
|
||||
params2["v"] = "1.0"
|
||||
params2["source_id"] = a.sourceID
|
||||
if params == nil {
|
||||
params2["body"] = ""
|
||||
} else {
|
||||
params2["body"] = string(utils.MustMarshal(params))
|
||||
}
|
||||
params2[signKey] = a.signParams(params2)
|
||||
params2Bytes := utils.MustMarshal(params2)
|
||||
request, _ := http.NewRequest("POST", a.url+action, bytes.NewReader(params2Bytes))
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
err = platformapi.AccessPlatformAPIWithRetry(a.client, request, a.config, func(response *http.Response) (result string, err error) {
|
||||
jsonResult1, err := utils.HTTPResponse2Json(response)
|
||||
if err != nil {
|
||||
return platformapi.ErrLevelGeneralFail, err
|
||||
}
|
||||
code := int(utils.MustInterface2Int64(jsonResult1["code"]))
|
||||
retVal = &ResponseResult{
|
||||
Code: code,
|
||||
ErrorCode: code,
|
||||
Msg: jsonResult1["msg"].(string),
|
||||
Status: jsonResult1["status"].(string),
|
||||
}
|
||||
if code == ResponseCodeSuccess {
|
||||
retVal.Result = jsonResult1["result"]
|
||||
return platformapi.ErrLevelSuccess, nil
|
||||
}
|
||||
baseapi.SugarLogger.Warnf("response business code is not ok, data:%v, code:%v", jsonResult1, code)
|
||||
newErr := utils.NewErrorIntCode(retVal.Msg, code)
|
||||
if code == ResponseCodeRetryLater || code == ResponseCodeNetworkErr {
|
||||
return platformapi.ErrLevelRecoverableErr, newErr
|
||||
}
|
||||
return platformapi.ErrLevelCodeIsNotOK, newErr
|
||||
})
|
||||
|
||||
return retVal, err
|
||||
}
|
||||
|
||||
func (a *API) GetCities() (retVal []City, err error) {
|
||||
result, err := a.AccessAPI("api/cityCode/list", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cites := result.Result.([]interface{})
|
||||
for _, v := range cites {
|
||||
v2 := v.(map[string]interface{})
|
||||
city := City{
|
||||
CityName: v2["cityName"].(string),
|
||||
CityCode: v2["cityCode"].(string),
|
||||
}
|
||||
retVal = append(retVal, city)
|
||||
}
|
||||
return retVal, nil
|
||||
}
|
||||
|
||||
func (a *API) GetCancelReasons() (retVal []CancelReason, err error) {
|
||||
result, err := a.AccessAPI("api/order/cancel/reasons", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cites := result.Result.([]interface{})
|
||||
for _, v := range cites {
|
||||
v2 := v.(map[string]interface{})
|
||||
reason := CancelReason{
|
||||
ID: int(utils.MustInterface2Int64(v2["id"])),
|
||||
Reason: v2["reason"].(string),
|
||||
}
|
||||
retVal = append(retVal, reason)
|
||||
}
|
||||
return retVal, nil
|
||||
}
|
||||
139
platformapi/dadaapi/dadaapi_test.go
Normal file
139
platformapi/dadaapi/dadaapi_test.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package dadaapi
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.rosy.net.cn/baseapi"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
dadaapi *API
|
||||
sugarLogger *zap.SugaredLogger
|
||||
testOrder *OperateOrderRequiredParams
|
||||
)
|
||||
|
||||
const (
|
||||
testShopNo = "11047059"
|
||||
)
|
||||
|
||||
func init() {
|
||||
logger, _ := zap.NewDevelopment()
|
||||
sugarLogger = logger.Sugar()
|
||||
baseapi.Init(sugarLogger)
|
||||
|
||||
// sandbox
|
||||
dadaapi = New("dada9623324449cd250", "30c2abbfe8a8780ad5aace46300c64b9", "73753", "http://callback.jxc4.com/dada/order", false)
|
||||
|
||||
// prod
|
||||
|
||||
testOrder = &OperateOrderRequiredParams{
|
||||
ShopNo: testShopNo,
|
||||
OriginID: "234242342",
|
||||
CityCode: "028",
|
||||
CargoPrice: 12.34,
|
||||
IsPrepay: 1,
|
||||
ReceiverName: "我是谁",
|
||||
ReceiverAddress: "九里堤",
|
||||
ReceiverLat: 30.74631,
|
||||
ReceiverLng: 103.99112,
|
||||
ReceiverPhone: "12812345678",
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestTest(t *testing.T) {
|
||||
sugarLogger.Debug(utils.GetCurTimeStr())
|
||||
}
|
||||
|
||||
func TestSignCallback(t *testing.T) {
|
||||
sampleData := `{"signature":"5a277f2519b6011028ff541fb09b8553","client_id":"275000419162381","order_id":"234242342","order_status":1,"cancel_reason":"","cancel_from":0,"dm_id":0,"update_time":1529564947}`
|
||||
mapData := make(map[string]interface{})
|
||||
utils.UnmarshalUseNumber([]byte(sampleData), &mapData)
|
||||
sign := dadaapi.signCallbackParams(mapData)
|
||||
if sign != mapData["signature"] {
|
||||
t.Fatal("sign is not correct")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessAPI(t *testing.T) {
|
||||
body := make(map[string]interface{})
|
||||
body["order_id"] = "fakeorderid"
|
||||
result, err := dadaapi.AccessAPI("api/order/status/query", body)
|
||||
|
||||
failed := true
|
||||
if err != nil {
|
||||
if err2, ok := err.(*utils.ErrorWithCode); ok {
|
||||
if err2.IntCode() != ResponseCodeSignErr {
|
||||
failed = false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
failed = false
|
||||
}
|
||||
|
||||
if failed {
|
||||
t.Fatalf("Error when accessing api result:%v, error:%v", result, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCites(t *testing.T) {
|
||||
result, err := dadaapi.GetCities()
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
failed := true
|
||||
for _, city := range result {
|
||||
if city.CityCode == "028" {
|
||||
failed = false
|
||||
}
|
||||
}
|
||||
|
||||
if failed {
|
||||
t.Fatal("failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetReasons(t *testing.T) {
|
||||
result, err := dadaapi.GetCancelReasons()
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
// baseapi.SugarLogger.Debug(result)
|
||||
failed := true
|
||||
for _, reason := range result {
|
||||
if reason.ID == 1 {
|
||||
failed = false
|
||||
}
|
||||
}
|
||||
|
||||
if failed {
|
||||
t.Fatal("failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddOrder(t *testing.T) {
|
||||
result, err := dadaapi.AddOrder(testOrder, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
baseapi.SugarLogger.Debug(result)
|
||||
}
|
||||
|
||||
func TestReaddOrder(t *testing.T) {
|
||||
result, err := dadaapi.ReaddOrder(testOrder, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
baseapi.SugarLogger.Debug(result)
|
||||
}
|
||||
|
||||
func TestCancelOrder(t *testing.T) {
|
||||
result, err := dadaapi.CancelOrder("234242342", ReasonIDClientDontWantItAnymore, "")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
baseapi.SugarLogger.Debug(result)
|
||||
}
|
||||
131
platformapi/dadaapi/order.go
Normal file
131
platformapi/dadaapi/order.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package dadaapi
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"github.com/fatih/structs"
|
||||
)
|
||||
|
||||
const (
|
||||
ReasonIDNobodyAccept = 1
|
||||
ReasonIDNobodyPickup = 2
|
||||
ReasonIDCourierIsPig = 3
|
||||
ReasonIDClientCanceled = 4
|
||||
ReasonIDOrderIsWrong = 5
|
||||
ReasonIDCourierWantMeCancel = 34
|
||||
ReasonIDCourierDontWantToPickup = 35
|
||||
ReasonIDClientDontWantItAnymore = 36
|
||||
ReasonIDCourierShirk = 37
|
||||
ReasonIDOther = 10000
|
||||
)
|
||||
|
||||
const (
|
||||
OrderStatusWaitingForAccept = 1
|
||||
OrderStatusPickingup = 2
|
||||
OrderStatusDeliverying = 3
|
||||
OrderStatusFinished = 4
|
||||
OrderStatusCanceled = 5
|
||||
OrderStatusExpired = 7
|
||||
OrderStatusAssignment = 8
|
||||
OrderStatusReturning = 9
|
||||
OrderStatusReturningFinished = 10
|
||||
OrderStatusAddOrderFailed = 1000
|
||||
)
|
||||
|
||||
type OperateOrderRequiredParams struct {
|
||||
ShopNo string `json:"shop_no"`
|
||||
OriginID string `json:"origin_id"`
|
||||
CityCode string `json:"city_code"`
|
||||
CargoPrice float64 `json:"cargo_price"`
|
||||
IsPrepay int `json:"is_prepay"`
|
||||
ReceiverName string `json:"receiver_name"`
|
||||
ReceiverAddress string `json:"receiver_address"`
|
||||
ReceiverLat float64 `json:"receiver_lat"`
|
||||
ReceiverLng float64 `json:"receiver_lng"`
|
||||
ReceiverPhone string `json:"receiver_phone"`
|
||||
ReceiverTel string `json:"receiver_tel"`
|
||||
}
|
||||
|
||||
type CreateOrderResponse struct {
|
||||
Distance float64
|
||||
Fee float64
|
||||
DeliverFee float64
|
||||
CouponFee float64
|
||||
Tips float64
|
||||
InsuranceFee float64
|
||||
}
|
||||
|
||||
type CancelOrderResponse struct {
|
||||
DeductFee float64 `json:"deduct_fee"`
|
||||
}
|
||||
|
||||
func (a *API) QueryOrderInfo(orderId string) (retVal map[string]interface{}, err error) {
|
||||
params := make(map[string]interface{})
|
||||
params["order_id"] = orderId
|
||||
result, err := a.AccessAPI("api/order/status/query", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result.Result.(map[string]interface{}), nil
|
||||
}
|
||||
|
||||
func map2CreateOrderResponse(mapData map[string]interface{}) *CreateOrderResponse {
|
||||
retVal := new(CreateOrderResponse)
|
||||
retVal.Distance = utils.MustInterface2Float64(mapData["distance"])
|
||||
retVal.Fee = utils.MustInterface2Float64(mapData["fee"])
|
||||
retVal.DeliverFee = utils.MustInterface2Float64(mapData["deliverFee"])
|
||||
|
||||
if value, ok := mapData["couponFee"]; ok {
|
||||
retVal.CouponFee = utils.MustInterface2Float64(value)
|
||||
}
|
||||
if value, ok := mapData["tips"]; ok {
|
||||
retVal.CouponFee = utils.MustInterface2Float64(value)
|
||||
}
|
||||
if value, ok := mapData["insuranceFee"]; ok {
|
||||
retVal.CouponFee = utils.MustInterface2Float64(value)
|
||||
}
|
||||
|
||||
return retVal
|
||||
}
|
||||
|
||||
func (a *API) operateOrder(action string, orderInfo *OperateOrderRequiredParams, addParams map[string]interface{}) (retVal *CreateOrderResponse, err error) {
|
||||
params := structs.Map(orderInfo)
|
||||
params["callback"] = a.callbackURL
|
||||
allParams := utils.MergeMaps(params, addParams)
|
||||
|
||||
result, err := a.AccessAPI(action, allParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return map2CreateOrderResponse(result.Result.(map[string]interface{})), nil
|
||||
}
|
||||
|
||||
func (a *API) AddOrder(orderInfo *OperateOrderRequiredParams, addParams map[string]interface{}) (retVal *CreateOrderResponse, err error) {
|
||||
return a.operateOrder("api/order/addOrder", orderInfo, addParams)
|
||||
}
|
||||
|
||||
func (a *API) ReaddOrder(orderInfo *OperateOrderRequiredParams, addParams map[string]interface{}) (retVal *CreateOrderResponse, err error) {
|
||||
return a.operateOrder("api/order/reAddOrder", orderInfo, addParams)
|
||||
}
|
||||
|
||||
func (a *API) QueryDeliverFee(orderInfo *OperateOrderRequiredParams, addParams map[string]interface{}) (retVal *CreateOrderResponse, err error) {
|
||||
return a.operateOrder("api/order/queryDeliverFee", orderInfo, addParams)
|
||||
}
|
||||
|
||||
func (a *API) AddOrderAfterQuery(orderInfo *OperateOrderRequiredParams, addParams map[string]interface{}) (retVal *CreateOrderResponse, err error) {
|
||||
return a.operateOrder("api/order/addAfterQuery", orderInfo, addParams)
|
||||
}
|
||||
|
||||
func (a *API) CancelOrder(orderId string, cancelOrderReasonId int, cancelOrderReason string) (retVal *CancelOrderResponse, err error) {
|
||||
mapData := utils.Params2Map("order_id", orderId, "cancel_reason_id", cancelOrderReasonId, "cancel_reason", cancelOrderReason)
|
||||
result, err := a.AccessAPI("api/order/formalCancel", mapData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mapResult := result.Result.(map[string]interface{})
|
||||
retVal = new(CancelOrderResponse)
|
||||
retVal.DeductFee = utils.MustInterface2Float64(mapResult["deduct_fee"])
|
||||
|
||||
return retVal, nil
|
||||
}
|
||||
Reference in New Issue
Block a user