200 lines
5.0 KiB
Go
200 lines
5.0 KiB
Go
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
|
|
}
|
|
}
|
|
}
|