- dadaapi added.

- refactor.
This commit is contained in:
gazebo
2018-06-21 14:23:01 +08:00
parent e1b377086e
commit 851b2c1eeb
10 changed files with 424 additions and 70 deletions

View File

@@ -6,6 +6,8 @@ import (
"net/http"
"time"
"github.com/fatih/structs"
"git.rosy.net.cn/baseapi"
"git.rosy.net.cn/baseapi/utils"
)
@@ -40,6 +42,10 @@ var (
CBErrMsgUnmarshal = "can not unmarshal data:%v, data:%v"
)
func init() {
structs.DefaultTagName = "json"
}
func AccessPlatformAPIWithRetry(params *AccessPlatformAPIWithRetryParams, handleResponse func(response *http.Response) (string, error)) error {
exceedLimitRetryCount := 0
recoverableErrorRetryCount := 0

View File

@@ -0,0 +1,83 @@
package dadaapi
import (
"crypto/md5"
"fmt"
"sort"
"strings"
"github.com/fatih/structs"
"git.rosy.net.cn/baseapi"
"git.rosy.net.cn/baseapi/utils"
)
const (
DadaResponseHttpCodeSuccess = 200
DadaResponseHttpCodeGeneralErr = 555
)
type DadaCallbackMsg 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 DadaCallbackResponse struct {
Code int
Dummy string `json:"dummy"`
}
var (
SuccessResponse = &DadaCallbackResponse{Code: DadaResponseHttpCodeSuccess}
FailedResponse = &DadaCallbackResponse{Code: DadaResponseHttpCodeGeneralErr}
)
func (d *DadaAPI) signParamsCallback(mapData map[string]interface{}) string {
values := make([]string, 0)
for k, v := range mapData {
if k != signKey {
values = append(values, fmt.Sprint(v))
}
}
sort.Strings(values)
finalStr := strings.Join(values, "")
// baseapi.SugarLogger.Debugf("sign str:%v", finalStr)
return fmt.Sprintf("%x", md5.Sum([]byte(finalStr)))
}
func (d *DadaAPI) unmarshalData(data []byte, msg interface{}) (callbackResponse *DadaCallbackResponse) {
err := utils.UnmarshalUseNumber(data, msg)
if err != nil {
return FailedResponse
}
return nil
}
func (d *DadaAPI) CheckCallbackValidation(mapData map[string]interface{}) (callbackResponse *DadaCallbackResponse) {
sign := d.signParamsCallback(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 (d *DadaAPI) GetOrderCallbackMsg(data []byte) (orderMsg *DadaCallbackMsg, callbackResponse *DadaCallbackResponse) {
orderMsg = new(DadaCallbackMsg)
if callbackResponse = d.unmarshalData(data, orderMsg); callbackResponse != nil {
return nil, FailedResponse
}
mapData := structs.Map(orderMsg)
callbackResponse = d.CheckCallbackValidation(mapData)
return orderMsg, callbackResponse
}

141
platform/dadaapi/dadaapi.go Normal file
View File

@@ -0,0 +1,141 @@
package dadaapi
import (
"bytes"
"crypto/md5"
"fmt"
"net/http"
"sort"
"time"
"git.rosy.net.cn/baseapi"
"git.rosy.net.cn/baseapi/platform/common"
"git.rosy.net.cn/baseapi/utils"
)
const (
dadaSandboxURL = "http://newopen.qa.imdada.cn/"
dadaProdURL = "http://newopen.imdada.cn/"
signKey = "signature"
)
const (
clientTimeout = time.Second * 10
sleepSecondWhenLimited = 6 * time.Second
maxRetryCountWhenNetworkException = 3
maxRetryCountWhenReachLimited = 10
)
const (
DadaCodeException = -1
DadaCodeSuccess = 0
DadaCodeSignErr = 2003
DadaCodeRetryLater = 2012
DadaCodeNetworkErr = 2455
)
type DadaAPI struct {
appKey string
appSecret string
sourceId string
url string
callbackURL string
client *http.Client
}
type DadaResult struct {
Status string `json:"status"`
Code int `json:"code"`
Msg string `json:"msg"`
Result map[string]interface{} `json:"result"`
ErrorCode int `json:"errorCode"`
}
func NewDadaAPI(appKey, appSecret, sourceId, callbackURL string, isProd bool) *DadaAPI {
api := &DadaAPI{
appKey: appKey,
appSecret: appSecret,
sourceId: sourceId,
callbackURL: callbackURL,
client: &http.Client{Timeout: clientTimeout},
}
if isProd {
api.url = dadaProdURL
} else {
api.url = dadaSandboxURL
}
return api
}
func (d *DadaAPI) signParams(mapData map[string]interface{}) string {
keys := make([]string, 0)
for k := range mapData {
keys = append(keys, k)
}
sort.Strings(keys)
finalStr := d.appSecret
for _, k := range keys {
if k != signKey {
finalStr += k + fmt.Sprint(mapData[k])
}
}
finalStr += d.appSecret
// baseapi.SugarLogger.Debugf("sign str:%v", finalStr)
return fmt.Sprintf("%X", md5.Sum([]byte(finalStr)))
}
func (d *DadaAPI) AccessDada(action string, params map[string]interface{}) (retVal *DadaResult, err error) {
params2 := make(map[string]interface{})
params2["app_key"] = d.appKey
params2["timestamp"] = utils.Int64ToStr(utils.GetCurTimestamp())
params2["format"] = "json"
params2["v"] = "1.0"
params2["source_id"] = d.sourceId
if params == nil {
params2["body"] = ""
} else {
params2["body"] = string(utils.MustMarshal(params))
}
params2[signKey] = d.signParams(params2)
params2Bytes := utils.MustMarshal(params2)
request, _ := http.NewRequest("POST", d.url+action, bytes.NewReader(params2Bytes))
request.Header.Set("Content-Type", "application/json")
apiAccess := &common.AccessPlatformAPIWithRetryParams{
MaxExceedLimitRetryCount: maxRetryCountWhenReachLimited,
MaxRecoverableRetryCount: maxRetryCountWhenNetworkException,
SleepSecondWhenExceedLimit: sleepSecondWhenLimited,
Client: d.client,
Request: request,
}
err = common.AccessPlatformAPIWithRetry(apiAccess, func(response *http.Response) (result string, err error) {
jsonResult1, err := utils.HttpResponse2Json(response)
if err != nil {
baseapi.SugarLogger.Warnf("HttpResponse2Json return:%v", err)
return common.PAErrorLevelGeneralFail, err
}
code := int(utils.MustInterface2Int64(jsonResult1["code"]))
retVal = &DadaResult{
Code: code,
ErrorCode: code,
Msg: jsonResult1["msg"].(string),
Status: jsonResult1["status"].(string),
}
if code == DadaCodeSuccess {
retVal.Result = jsonResult1["result"].(map[string]interface{})
return common.PAErrorLevelSuccess, nil
}
baseapi.SugarLogger.Debug(jsonResult1)
if code == DadaCodeRetryLater || code == DadaCodeNetworkErr {
return common.PAErrorLevelRecoverable, nil
}
return common.PAErrorLevelGeneralFail, utils.NewErrorIntCode(retVal.Msg, code)
})
return retVal, err
}

View File

@@ -0,0 +1,51 @@
package dadaapi
import (
"testing"
"git.rosy.net.cn/baseapi"
"git.rosy.net.cn/baseapi/utils"
"go.uber.org/zap"
)
var (
dadaapi *DadaAPI
sugarLogger *zap.SugaredLogger
)
func init() {
logger, _ := zap.NewDevelopment()
sugarLogger = logger.Sugar()
baseapi.Init(sugarLogger)
// sandbox
dadaapi = NewDadaAPI("dada9623324449cd250", "30c2abbfe8a8780ad5aace46300c64b9", "73753", "", false)
// prod
}
func TestTest(t *testing.T) {
sugarLogger.Debug(utils.GetCurTimeStr())
}
func TestAccessDada(t *testing.T) {
body := make(map[string]interface{})
body["order_id"] = "fakeorderid"
result, err := dadaapi.AccessDada("api/order/status/query", body)
failed := true
if err != nil {
if err2, ok := err.(*utils.ErrorWithCode); ok {
if err2.IntCode() != DadaCodeSignErr {
failed = false
}
}
} else {
failed = false
}
if failed {
t.Fatalf("Error when accessing api result:%v, error:%v", result, err)
}
}

85
platform/dadaapi/order.go Normal file
View File

@@ -0,0 +1,85 @@
package dadaapi
import (
"git.rosy.net.cn/baseapi/utils"
"github.com/fatih/structs"
)
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"`
}
type CreateOrderResponse struct {
Distance float64
Fee float64
DeliverFee float64
CouponFee float64
Tips float64
InsuranceFee float64
}
func (d *DadaAPI) QueryOrderInfo(orderId string) (retVal map[string]interface{}, err error) {
params := make(map[string]interface{})
params["order_id"] = orderId
result, err := d.AccessDada("api/order/status/query", params)
if err != nil {
return nil, err
}
return result.Result, 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 (d *DadaAPI) operateOrder(action string, orderInfo *OperateOrderRequiredParams, addParams map[string]interface{}) (retVal *CreateOrderResponse, err error) {
params := structs.Map(orderInfo)
params["callback"] = d.callbackURL
allParams := utils.MergeMaps(params, addParams)
result, err := d.AccessDada(action, allParams)
if err != nil {
return nil, err
}
return map2CreateOrderResponse(result.Result), nil
}
func (d *DadaAPI) AddOrder(orderInfo *OperateOrderRequiredParams, addParams map[string]interface{}) (retVal *CreateOrderResponse, err error) {
return d.operateOrder("api/order/addOrder", orderInfo, addParams)
}
func (d *DadaAPI) ReaddOrder(orderInfo *OperateOrderRequiredParams, addParams map[string]interface{}) (retVal *CreateOrderResponse, err error) {
return d.operateOrder("api/order/reAddOrder", orderInfo, addParams)
}
func (d *DadaAPI) QueryDeliverFee(orderInfo *OperateOrderRequiredParams, addParams map[string]interface{}) (retVal *CreateOrderResponse, err error) {
return d.operateOrder("api/order/queryDeliverFee", orderInfo, addParams)
}
func (d *DadaAPI) AddOrderAfterQuery(orderInfo *OperateOrderRequiredParams, addParams map[string]interface{}) (retVal *CreateOrderResponse, err error) {
return d.operateOrder("api/order/addAfterQuery", orderInfo, addParams)
}

View File

@@ -23,14 +23,14 @@ type ELMCallbackResponse struct {
}
type ELMCallbackMsg struct {
AppId int `json:"appId" structs:"appId"`
RequestId string `json:"requestId" structs:"requestId"`
Type int `json:"type" structs:"type"`
Message string `json:"message" structs:"message"`
ShopId int `json:"shopId" structs:"shopId"`
Timestamp int64 `json:"timestamp" structs:"timestamp"`
UserId int64 `json:"userId" structs:"userId"`
Signature string `json:"signature" structs:"signature"`
AppId int `json:"appId"`
RequestId string `json:"requestId"`
Type int `json:"type"`
Message string `json:"message"`
ShopId int `json:"shopId"`
Timestamp int64 `json:"timestamp"`
UserId int64 `json:"userId"`
Signature string `json:"signature"`
}
var (
@@ -47,23 +47,22 @@ func (e *ELMAPI) unmarshalData(data []byte, msg interface{}) (callbackResponse *
return nil
}
func (e *ELMAPI) CheckRequestValidation(mapData map[string]interface{}) (callbackResponse *ELMCallbackResponse) {
func (e *ELMAPI) CheckCallbackValidation(mapData map[string]interface{}) (callbackResponse *ELMCallbackResponse) {
sign := e.signParamsMap(mapData, "")
if remoteSign, ok := mapData[signKey].(string); ok && sign != remoteSign {
if remoteSign, _ := mapData[signKey].(string); sign != remoteSign {
baseapi.SugarLogger.Infof("Signature is not ok, mine:%v, get:%v", sign, remoteSign)
return &ELMCallbackResponse{Message: "signature is invalid"}
}
return nil
}
func (e *ELMAPI) GetMsgFromData(data []byte) (msg *ELMCallbackMsg, callbackResponse *ELMCallbackResponse) {
func (e *ELMAPI) GetCallbackMsg(data []byte) (msg *ELMCallbackMsg, callbackResponse *ELMCallbackResponse) {
msg = new(ELMCallbackMsg)
callbackResponse = e.unmarshalData(data, msg)
if callbackResponse != nil {
if callbackResponse = e.unmarshalData(data, msg); callbackResponse != nil {
return nil, callbackResponse
}
mapData := structs.Map(msg)
callbackResponse = e.CheckRequestValidation(mapData)
callbackResponse = e.CheckCallbackValidation(mapData)
return msg, callbackResponse
}

View File

@@ -20,10 +20,10 @@ func init() {
baseapi.Init(sugarLogger)
// sandbox
elmapi = NewELMAPI("2d2b583447b04b6bba5a6f3faed3559b", "RwT214gAsS", "56afff4b9ebd8a7eb532d18fa33f17be57f9b9db", false)
// elmapi = NewELMAPI("b4f7e424475c3758c111dc60ceec3e2a", "RwT214gAsS", "56afff4b9ebd8a7eb532d18fa33f17be57f9b9db", false)
// prod
// elmapi = NewELMAPI("bab2a27f99562f394b411dbb9a6214da", "KLRDcOZGrk", "1fc221f8265506531da36fb613d5f5ad673f2e9a", true)
elmapi = NewELMAPI("bab2a27f99562f394b411dbb9a6214da", "KLRDcOZGrk", "1fc221f8265506531da36fb613d5f5ad673f2e9a", true)
}
func TestTest(t *testing.T) {
@@ -57,7 +57,7 @@ func TestGetOrder(t *testing.T) {
func TestCallbackSign(t *testing.T) {
jsonStr := `{"requestId":"200016348669063447","type":18,"appId":78247922,"message":"{\"orderId\":\"3024923917769149510\",\"state\":\"settled\",\"shopId\":157492364,\"updateTime\":1529465510,\"role\":1}","shopId":157492364,"timestamp":1529465510255,"signature":"D65F917D93B4F599B85486C799599141","userId":336072266322770688}`
msg, response := elmapi.GetMsgFromData([]byte(jsonStr))
msg, response := elmapi.GetCallbackMsg([]byte(jsonStr))
if response != nil || msg == nil {
t.Fatal("Something wrong")
}

View File

@@ -56,7 +56,7 @@ func (j *JDAPI) unmarshalData(strData string, msg interface{}) (callbackResponse
return nil
}
func (j *JDAPI) CheckRequestValidation(request *http.Request) (callbackResponse *JDCallbackResponse) {
func (j *JDAPI) CheckCallbackValidation(request *http.Request) (callbackResponse *JDCallbackResponse) {
mapData := make(map[string]string)
mapData["token"] = request.FormValue("token")
mapData["app_key"] = request.FormValue("app_key")
@@ -78,53 +78,44 @@ func (j *JDAPI) CheckRequestValidation(request *http.Request) (callbackResponse
return nil
}
func (j *JDAPI) getCommonOrderMsg(request *http.Request, needDecode bool) (msg *JDOrderMsg, callbackResponse *JDCallbackResponse) {
if callbackResponse = j.CheckRequestValidation(request); callbackResponse != nil {
return nil, callbackResponse
func (j *JDAPI) getCommonOrderCallbackMsg(request *http.Request, msg interface{}, needDecode bool) (callbackResponse *JDCallbackResponse) {
if callbackResponse = j.CheckCallbackValidation(request); callbackResponse != nil {
return callbackResponse
}
msg = new(JDOrderMsg)
jdParamJSON := request.FormValue(JD_PARAM_JSON)
if needDecode {
if jdParamJSON2, err := url.QueryUnescape(jdParamJSON); err == nil {
jdParamJSON = jdParamJSON2
} else {
return &JDCallbackResponse{
Code: JDerrorCodeAbnormalParam,
Msg: fmt.Sprintf(common.CBErrMsgUnescape, jdParamJSON, err),
Data: jdParamJSON,
}
}
}
callbackResponse = j.unmarshalData(jdParamJSON, msg)
if callbackResponse != nil {
return nil, callbackResponse
if callbackResponse = j.unmarshalData(jdParamJSON, msg); callbackResponse != nil {
return callbackResponse
}
return msg, nil
return nil
}
func (j *JDAPI) GetOrderMsg(request *http.Request) (msg *JDOrderMsg, callbackResponse *JDCallbackResponse) {
return j.getCommonOrderMsg(request, false)
func (j *JDAPI) GetOrderCallbackMsg(request *http.Request) (msg *JDOrderMsg, callbackResponse *JDCallbackResponse) {
msg = new(JDOrderMsg)
callbackResponse = j.getCommonOrderCallbackMsg(request, msg, false)
return msg, callbackResponse
}
func (j *JDAPI) GetOrderApplyCancelMsg(request *http.Request) (msg *JDOrderMsg, callbackResponse *JDCallbackResponse) {
return j.getCommonOrderMsg(request, true)
func (j *JDAPI) GetOrderApplyCancelCallbackMsg(request *http.Request) (msg *JDOrderMsg, callbackResponse *JDCallbackResponse) {
msg = new(JDOrderMsg)
callbackResponse = j.getCommonOrderCallbackMsg(request, msg, true)
return msg, callbackResponse
}
func (j *JDAPI) GetOrderDeliveryMsg(request *http.Request) (msg *JDDeliveryStatusMsg, callbackResponse *JDCallbackResponse) {
if callbackResponse = j.CheckRequestValidation(request); callbackResponse != nil {
return nil, callbackResponse
}
func (j *JDAPI) GetOrderDeliveryCallbackMsg(request *http.Request) (msg *JDDeliveryStatusMsg, callbackResponse *JDCallbackResponse) {
msg = new(JDDeliveryStatusMsg)
jdParamJSON := request.FormValue(JD_PARAM_JSON)
jdParamJSON2, err := url.QueryUnescape(jdParamJSON)
if err != nil {
return nil, &JDCallbackResponse{
Code: JDerrorCodeAbnormalParam,
Msg: fmt.Sprintf(common.CBErrMsgUnescape, jdParamJSON, err),
Data: jdParamJSON,
}
}
jdParamJSON = jdParamJSON2
callbackResponse = j.unmarshalData(jdParamJSON, msg)
if callbackResponse != nil {
return nil, callbackResponse
}
return msg, nil
callbackResponse = j.getCommonOrderCallbackMsg(request, msg, true)
return msg, callbackResponse
}

View File

@@ -39,7 +39,7 @@ var (
SignatureIsNotOk = &MtpsCallbackResponse{Code: -1}
)
func (m *MTPSAPI) CheckRequestValidation(request *http.Request) (callbackResponse *MtpsCallbackResponse) {
func (m *MTPSAPI) CheckCallbackValidation(request *http.Request) (callbackResponse *MtpsCallbackResponse) {
request.ParseForm()
sign := m.signParams(request.PostForm)
if sign != request.FormValue(signKey) {
@@ -59,8 +59,7 @@ func (m *MTPSAPI) CheckRequestValidation(request *http.Request) (callbackRespons
}
func (m *MTPSAPI) GetOrderCallbackMsg(request *http.Request) (orderMsg *MtpsCallbackOrderMsg, callbackResponse *MtpsCallbackResponse) {
callbackResponse = m.CheckRequestValidation(request)
if callbackResponse != nil {
if callbackResponse = m.CheckCallbackValidation(request); callbackResponse != nil {
return nil, callbackResponse
}
orderMsg = &MtpsCallbackOrderMsg{
@@ -84,8 +83,7 @@ func (m *MTPSAPI) GetOrderCallbackMsg(request *http.Request) (orderMsg *MtpsCall
}
func (m *MTPSAPI) GetOrderExceptionCallbackMsg(request *http.Request) (orderMsg *MtpsCallbackOrderExceptionMsg, callbackResponse *MtpsCallbackResponse) {
callbackResponse = m.CheckRequestValidation(request)
if callbackResponse != nil {
if callbackResponse = m.CheckCallbackValidation(request); callbackResponse != nil {
return nil, callbackResponse
}
orderMsg = &MtpsCallbackOrderExceptionMsg{

View File

@@ -107,20 +107,20 @@ type MTPSResult struct {
}
type MtpsCreateOrderByShopInfo struct {
DeliveryId int64 `structs:"delivery_id"`
OrderId string `structs:"order_id"`
ShopId string `structs:"shop_id"`
DeliveryServiceCode int `structs:"delivery_service_code"`
ReceiverName string `structs:"receiver_name"`
ReceiverAddress string `structs:"receiver_address"`
ReceiverPhone string `structs:"receiver_phone"`
ReceiverLng int `structs:"receiver_lng"`
ReceiverLat int `structs:"receiver_lat"`
CoordinateType int `structs:"coordinate_type"`
GoodsValue float64 `structs:"goods_value"`
GoodsWeight float64 `structs:"goods_weight"`
ExpectedDeliveryTime int64 `structs:"expected_delivery_time"`
OrderType int `structs:"order_type"`
DeliveryId int64 `json:"delivery_id"`
OrderId string `json:"order_id"`
ShopId string `json:"shop_id"`
DeliveryServiceCode int `json:"delivery_service_code"`
ReceiverName string `json:"receiver_name"`
ReceiverAddress string `json:"receiver_address"`
ReceiverPhone string `json:"receiver_phone"`
ReceiverLng int `json:"receiver_lng"`
ReceiverLat int `json:"receiver_lat"`
CoordinateType int `json:"coordinate_type"`
GoodsValue float64 `json:"goods_value"`
GoodsWeight float64 `json:"goods_weight"`
ExpectedDeliveryTime int64 `json:"expected_delivery_time"`
OrderType int `json:"order_type"`
}
type MTPSAPI struct {