- basic elm api added.
This commit is contained in:
199
platform/elmapi/elmapi.go
Normal file
199
platform/elmapi/elmapi.go
Normal file
@@ -0,0 +1,199 @@
|
||||
package elmapi
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
ELM_API_URL_SANDBOX = "https://open-api-sandbox.shop.ele.me/api/v1/"
|
||||
ELM_API_URL_PROD = "https://open-api.shop.ele.me/api/v1/"
|
||||
)
|
||||
|
||||
const (
|
||||
sleepSecondWhenLimited = 6 * time.Second
|
||||
maxRetryCountWhenNetworkException = 3
|
||||
maxRetryCountWhenReachLimited = 10
|
||||
)
|
||||
|
||||
var (
|
||||
ErrSystemErrMaxRetry = errors.New("ELM System error reach max retry count!")
|
||||
ErrLimitReachMaxRetry = errors.New("ELM Reach max retry count!")
|
||||
ErrHttpCode = errors.New("ELM HTTP Code is not 200")
|
||||
ErrELMCode = errors.New("ELM code is not 0")
|
||||
)
|
||||
|
||||
type ELMResult struct {
|
||||
Id string
|
||||
Result map[string]interface{}
|
||||
Error map[string]interface{}
|
||||
}
|
||||
|
||||
type ELMAPI struct {
|
||||
token string
|
||||
appKey string
|
||||
secret string
|
||||
sugarLogger *zap.SugaredLogger
|
||||
url *url.URL
|
||||
client http.Client
|
||||
}
|
||||
|
||||
type ELMPayload struct {
|
||||
Token string `json:"token"`
|
||||
Nop string `json:"nop"`
|
||||
Metas map[string]interface{} `json:"metas"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
Action string `json:"action"`
|
||||
Id string `json:"id"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
func NewELMAPI(token, appKey, secret string, sugarLogger *zap.SugaredLogger, isProd bool) *ELMAPI {
|
||||
api := &ELMAPI{
|
||||
token: token,
|
||||
appKey: appKey,
|
||||
secret: secret,
|
||||
sugarLogger: sugarLogger,
|
||||
client: http.Client{Timeout: time.Second * 10},
|
||||
}
|
||||
|
||||
if isProd {
|
||||
api.url, _ = url.Parse(ELM_API_URL_PROD)
|
||||
} else {
|
||||
api.url, _ = url.Parse(ELM_API_URL_SANDBOX)
|
||||
}
|
||||
return api
|
||||
}
|
||||
|
||||
func (e *ELMAPI) signParams(action string, payload *ELMPayload) string {
|
||||
keyValues := make([]string, 0)
|
||||
allData := []map[string]interface{}{
|
||||
payload.Metas,
|
||||
payload.Params,
|
||||
}
|
||||
for _, data := range allData {
|
||||
for k, v := range data {
|
||||
vBytes, _ := json.Marshal(v)
|
||||
keyValues = append(keyValues, k+"="+string(vBytes))
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(keyValues)
|
||||
finalStr := action + e.token + strings.Join(keyValues, "") + e.secret
|
||||
// e.sugarLogger.Debugf("sign str:%v", finalStr)
|
||||
return fmt.Sprintf("%X", md5.Sum([]byte(finalStr)))
|
||||
}
|
||||
|
||||
func (e *ELMAPI) AccessELM(action string, params map[string]interface{}) (*ELMResult, error) {
|
||||
if params == nil {
|
||||
params = make(map[string]interface{}, 0)
|
||||
}
|
||||
metas := map[string]interface{}{
|
||||
"app_key": e.appKey,
|
||||
"timestamp": int(utils.GetCurTimestamp()),
|
||||
}
|
||||
|
||||
payload := &ELMPayload{
|
||||
Token: e.token,
|
||||
Nop: "1.0.0",
|
||||
Metas: metas,
|
||||
Params: params,
|
||||
Action: action,
|
||||
Id: utils.GetUUID(),
|
||||
}
|
||||
|
||||
payload.Signature = e.signParams(action, payload)
|
||||
dataBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
e.sugarLogger.Errorf("Error when marshal %v, error:%v", payload, err)
|
||||
return nil, err
|
||||
}
|
||||
dataStr := string(dataBytes)
|
||||
exceedLimitRetryCount := 0
|
||||
systemErrorRetryCount := 0
|
||||
|
||||
for {
|
||||
request := &http.Request{
|
||||
Method: "POST",
|
||||
URL: e.url,
|
||||
Header: http.Header{
|
||||
"Content-Type": []string{"application/json; charset=utf-8"},
|
||||
"Content-Encoding": []string{"gzip, deflate"},
|
||||
"User-Agent": []string{"eleme-golang-api"},
|
||||
// "x-eleme-requestid": []string{payload.Id},
|
||||
},
|
||||
|
||||
Body: ioutil.NopCloser(strings.NewReader(dataStr)),
|
||||
}
|
||||
response, err := e.client.Do(request)
|
||||
if err != nil {
|
||||
e.sugarLogger.Debugf("client.Get return err:%v", err)
|
||||
err, ok := err.(net.Error)
|
||||
systemErrorRetryCount++
|
||||
if ok && err.Timeout() && systemErrorRetryCount <= maxRetryCountWhenNetworkException {
|
||||
continue
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode != 200 {
|
||||
e.sugarLogger.Debugf("http code is:%d", response.StatusCode)
|
||||
systemErrorRetryCount++
|
||||
if systemErrorRetryCount <= maxRetryCountWhenNetworkException {
|
||||
continue
|
||||
}
|
||||
|
||||
return nil, ErrHttpCode
|
||||
}
|
||||
|
||||
jsonResult1, err := utils.HttpResponse2Json(response)
|
||||
resultError, _ := jsonResult1["error"].(map[string]interface{})
|
||||
result, _ := jsonResult1["result"].(map[string]interface{})
|
||||
jsonResult := &ELMResult{
|
||||
Id: jsonResult1["id"].(string),
|
||||
Error: resultError,
|
||||
Result: result,
|
||||
}
|
||||
if err != nil {
|
||||
e.sugarLogger.Warnf("HttpResponse2Json return:%v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
errinfoMap := jsonResult.Error
|
||||
if errinfoMap == nil {
|
||||
return jsonResult, nil
|
||||
}
|
||||
errCode := errinfoMap["code"].(string)
|
||||
|
||||
if errCode == "EXCEED_LIMIT" {
|
||||
exceedLimitRetryCount++
|
||||
if exceedLimitRetryCount <= maxRetryCountWhenReachLimited {
|
||||
time.Sleep(sleepSecondWhenLimited)
|
||||
} else {
|
||||
return jsonResult, ErrLimitReachMaxRetry
|
||||
}
|
||||
} else if errCode == "SERVER_ERROR" || errCode == "BIZ_SYSTEM_ERROR" || errCode == "BIZ_1006" || errCode == "BUSINESS_ERROR" {
|
||||
if systemErrorRetryCount <= maxRetryCountWhenNetworkException {
|
||||
continue
|
||||
}
|
||||
return jsonResult, ErrSystemErrMaxRetry
|
||||
} else {
|
||||
e.sugarLogger.Debug(jsonResult)
|
||||
return jsonResult, ErrELMCode
|
||||
}
|
||||
}
|
||||
}
|
||||
37
platform/elmapi/elmapi_test.go
Normal file
37
platform/elmapi/elmapi_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package elmapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
elmapi *ELMAPI
|
||||
sugarLogger *zap.SugaredLogger
|
||||
)
|
||||
|
||||
func init() {
|
||||
logger, _ := zap.NewDevelopment()
|
||||
sugarLogger = logger.Sugar()
|
||||
elmapi = NewELMAPI("bab2a27f99562f394b411dbb9a6214da", "KLRDcOZGrk", "1fc221f8265506531da36fb613d5f5ad673f2e9a", sugarLogger, true)
|
||||
}
|
||||
|
||||
func TestTest(t *testing.T) {
|
||||
sugarLogger.Debug(utils.GetCurTimeStr())
|
||||
}
|
||||
|
||||
func TestAccessELM(t *testing.T) {
|
||||
result, err := elmapi.AccessELM("eleme.user.getUser", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error when accessing AccessJDQuery result:%v, error:%v", result, err)
|
||||
} else {
|
||||
userIdNumber := result.Result["userId"].(json.Number)
|
||||
userId, err := userIdNumber.Int64()
|
||||
if userId != 336072266326420104 || err != nil {
|
||||
t.Fatalf("userId is not correct:%v", userIdNumber)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -57,11 +56,9 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrJdParam = errors.New("can not marshal jd param")
|
||||
ErrSystemErrMaxRetry = errors.New("JD System error reach max retry count!")
|
||||
ErrLimitReachMaxRetry = errors.New("JD Reach max retry count!")
|
||||
ErrHttpCode = errors.New("JD HTTP Code is not 200")
|
||||
ErrFormatWrong = errors.New("JD Result format is strange!")
|
||||
ErrJDCode = errors.New("JD code is not 0")
|
||||
ErrInnerCodeIsNotOk = errors.New("JD result inner code is not ok")
|
||||
|
||||
@@ -146,12 +143,11 @@ func (j *JDAPI) AccessJDQuery(apiStr string, jdParams map[string]string) (map[st
|
||||
jdParams = make(map[string]string, 0)
|
||||
}
|
||||
jdParamStr, err := json.Marshal(jdParams)
|
||||
if err == nil {
|
||||
params["jd_param_json"] = string(jdParamStr)
|
||||
} else {
|
||||
return nil, ErrJdParam
|
||||
if err != nil {
|
||||
j.sugarLogger.Errorf("Error when marshal %v, error:%v", jdParams, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params["jd_param_json"] = string(jdParamStr)
|
||||
params["timestamp"] = utils.GetCurTimeStr()
|
||||
sign := signParams(params)
|
||||
params["sign"] = sign
|
||||
@@ -184,16 +180,10 @@ func (j *JDAPI) AccessJDQuery(apiStr string, jdParams map[string]string) (map[st
|
||||
return nil, ErrHttpCode
|
||||
}
|
||||
|
||||
var jsonResult map[string]interface{}
|
||||
bodyData, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
j.sugarLogger.Debugf("ioutil.ReadAll return:%v", err)
|
||||
return nil, err
|
||||
}
|
||||
jsonResult, err := utils.HttpResponse2Json(response)
|
||||
|
||||
err = utils.UnmarshalUseNumber(bodyData, &jsonResult)
|
||||
if err != nil {
|
||||
j.sugarLogger.Debugf("json.Unmarshal return:%v", err)
|
||||
j.sugarLogger.Warnf("HttpResponse2Json return:%v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -141,3 +143,17 @@ func GetCurTimestamp() int64 {
|
||||
func GetAPIOperator() string {
|
||||
return time.Now().Format("2006-01-02_15:04:05")
|
||||
}
|
||||
|
||||
func HttpResponse2Json(response *http.Response) (map[string]interface{}, error) {
|
||||
var jsonResult map[string]interface{}
|
||||
bodyData, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = UnmarshalUseNumber(bodyData, &jsonResult)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return jsonResult, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user