- big big refactor.

This commit is contained in:
gazebo
2018-06-22 22:26:12 +08:00
parent 062f7a2540
commit 7657966da5
27 changed files with 1277 additions and 1266 deletions

View 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
}

View 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
}

View 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)
}

View 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
}