Files
baseapi/platformapi/mtwmapi/mtwmapi.go

192 lines
5.4 KiB
Go

package mtwmapi
import (
"bytes"
"crypto/md5"
"fmt"
"mime/multipart"
"net/http"
"net/url"
"sort"
"strings"
"sync"
"time"
"git.rosy.net.cn/baseapi"
"git.rosy.net.cn/baseapi/platformapi"
"git.rosy.net.cn/baseapi/utils"
)
const (
apiURL = "https://waimaiopen.meituan.com/api/v1"
sandboxAPIURL = "http://openapi.b.waimai.test.sankuai.com/api/v1"
signKey = "sig"
)
const (
KeyAppPoiCode = "app_poi_code"
KeyAppPoiCodes = "app_poi_codes"
KeyAppFoodCode = "app_food_code"
KeyImgName = "img_name"
KeyImgData = "img_data"
KeyOrderID = "order_id"
)
const (
resultKeyData = "data"
resultKeyMsg = "msg"
resultKeySuccessMsg = "success_msg"
)
const (
GeneralMaxLimit = 200 // 大多数的API的批处理最大条数
)
const (
ErrCodeSysErr = 700 // 系统错误,按美团外卖技术支持的说法,可当成需重试的错误
ErrCodeAccessLimited = 711 // 接口调用过于频繁,触发流控,请降低调用频率
ErrCodeNoAppFood = 805 // 不存在此菜品
ErrCodeNoSuchOrder = 808 // 不存在此订单
ErrCodeSkuCategoryNotExist = 1021 // 菜品分类不存在
ErrCodeSkuCategoryExist = 1037 // 菜品分类已存在
)
type API struct {
platformapi.APICookie
appID string
secret string
callbackURL string
client *http.Client
config *platformapi.APIConfig
locker sync.RWMutex
userCookies map[string]string
}
var (
canRetryCodes = map[int]int{
ErrCodeSysErr: 1,
ErrCodeAccessLimited: 1,
}
)
func New(appID, secret, callbackURL string, config ...*platformapi.APIConfig) *API {
curConfig := platformapi.DefAPIConfig
if len(config) > 0 {
curConfig = *config[0]
}
return &API{
appID: appID,
secret: secret,
callbackURL: callbackURL,
client: &http.Client{Timeout: curConfig.ClientTimeout},
config: &curConfig,
userCookies: make(map[string]string),
}
}
func (a *API) genURL(cmd string) string {
return apiURL + "/" + cmd
}
func (a *API) signParams(signURL string, params map[string]interface{}) string {
keys := make([]string, 0)
for k := range params {
if k != signKey {
keys = append(keys, k)
}
}
sort.Strings(keys)
finalStr := signURL
kvPaires := make([]string, len(keys))
for k, key := range keys {
if params[key] != nil {
kvPaires[k] = key + "=" + fmt.Sprintf("%v", params[key])
}
}
finalStr += strings.Join(kvPaires, "&") + a.secret
// baseapi.SugarLogger.Debug(finalStr)
return fmt.Sprintf("%x", md5.Sum([]byte(finalStr)))
}
func (a *API) AccessAPI2(cmd string, isGet bool, bizParams map[string]interface{}, resultKey, trackInfo string) (retVal interface{}, err error) {
params := make(map[string]interface{})
params["timestamp"] = time.Now().Unix()
params["app_id"] = a.appID
params = utils.MergeMaps(params, bizParams)
imgData := params[KeyImgData]
if imgData != nil {
delete(params, KeyImgData)
}
signURL := a.genURL(cmd) + "?"
params[signKey] = a.signParams(signURL, params)
err = platformapi.AccessPlatformAPIWithRetry(a.client,
func() *http.Request {
var request *http.Request
if isGet {
fullURL := utils.GenerateGetURL(apiURL, cmd, params)
// baseapi.SugarLogger.Debug(fullURL)
request, _ = http.NewRequest(http.MethodGet, fullURL, nil)
} else {
fullURL := a.genURL(cmd)
// baseapi.SugarLogger.Debug(utils.Map2URLValues(params).Encode())
if imgData == nil {
request, _ = http.NewRequest(http.MethodPost, fullURL, strings.NewReader(utils.Map2URLValues(params).Encode()))
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
} else {
var b bytes.Buffer
w := multipart.NewWriter(&b)
if fw, err := w.CreateFormFile("file", params[KeyImgName].(string)); err != nil {
panic(err.Error())
} else {
fw.Write(imgData.([]byte))
}
for k, v := range params {
fmt.Println(k, " ", v)
w.WriteField(k, url.QueryEscape(fmt.Sprint(v)))
}
w.Close()
// b.WriteString(utils.Map2URLValues(params).Encode())
request, _ = http.NewRequest(http.MethodPost, fullURL, &b)
request.Header.Set("Content-Type", w.FormDataContentType())
}
request.Header.Set("charset", "UTF-8")
}
if trackInfo != "" {
request.Header.Set(platformapi.KeyTrackInfo, trackInfo)
}
// request.Close = true //todo 为了性能考虑还是不要关闭
return request
},
a.config,
func(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) {
if jsonResult1 == nil {
return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil")
}
retVal = jsonResult1[resultKey]
if errObj, ok := jsonResult1["error"]; ok {
baseapi.SugarLogger.Debugf("mtwm AccessAPI failed, jsonResult1:%s", utils.Format4Output(jsonResult1, true))
errorInfo := errObj.(map[string]interface{})
newErr := utils.NewErrorIntCode(errorInfo["msg"].(string), int(utils.MustInterface2Int64(errorInfo["code"])))
if canRetryCodes[newErr.IntCode()] == 1 {
return platformapi.ErrLevelExceedLimit, newErr
}
return platformapi.ErrLevelCodeIsNotOK, newErr
}
return platformapi.ErrLevelSuccess, nil
})
err = platformapi.RebuildError(err, bizParams, []string{
KeyAppPoiCode,
KeyAppPoiCodes,
KeyAppFoodCode,
})
return retVal, err
}
func (a *API) AccessAPI(cmd string, isGet bool, bizParams map[string]interface{}) (retVal interface{}, err error) {
return a.AccessAPI2(cmd, isGet, bizParams, resultKeyData, "")
}