- basic elm api added.

This commit is contained in:
gazebo
2018-06-13 17:10:41 +08:00
parent 47ecf4263d
commit 5f6865666a
4 changed files with 258 additions and 16 deletions

199
platform/elmapi/elmapi.go Normal file
View 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
}
}
}

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

View File

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

View File

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