- weimob wsc api

This commit is contained in:
gazebo
2019-01-18 09:17:02 +08:00
parent 0d4bd09589
commit e06ec24013
6 changed files with 486 additions and 23 deletions

View File

@@ -58,6 +58,10 @@ const (
ErrLevelCodeIsNotOK = "JXC4_CODE_IS_NOT_OK"
)
const (
maxDataSizeDontOutput = 200 * 1024
)
// common api access error
var (
ErrAPIAccessFailed = errors.New("access API failed")
@@ -77,7 +81,11 @@ func init() {
}
func getClonedData(r *bytes.Buffer) string {
return string(r.Bytes())
retVal := string(r.Bytes())
if len(retVal) > maxDataSizeDontOutput {
return "request data is too large"
}
return retVal
}
func AccessPlatformAPIWithRetry(client *http.Client, handleRequest func() *http.Request, config *APIConfig, handleResponse func(bodyMap map[string]interface{}) (string, error)) error {

View File

@@ -1,9 +1,62 @@
package weimobapi
import (
"fmt"
"io/ioutil"
"net/http"
"git.rosy.net.cn/baseapi/platformapi"
"git.rosy.net.cn/baseapi/utils"
)
const (
DeductStockTypePlaceOrder = 1
DeductStockTypePay = 2
)
type PendingSaveB2CGoodsVo struct {
FreightTemplateId int64 `json:"freightTemplateId"`
DeliveryTypeIdList []int64 `json:"deliveryTypeIdList"`
B2cGoodsType int `json:"b2cGoodsType"`
}
type PendingSaveB2CSkuVo struct {
Weight float32 `json:"weight"`
Volume float32 `json:"volume"`
}
type PendingSaveSkuVo struct {
OuterSkuCode string `json:"outerSkuCode"`
ImageURL string `json:"imageUrl"`
SalePrice float32 `json:"salePrice"`
OriginalPrice float32 `json:"originalPrice"`
CostPrice float32 `json:"costPrice"`
EditStockNum int `json:"editStockNum"`
B2cSku *PendingSaveB2CSkuVo `json:"b2cSku"`
}
type Category struct {
CategoryID int64 `json:"categoryId"`
Title string `json:"title"`
Level int `json:"level"`
ParentID int64 `json:"parentId"`
}
type GoodsClassify struct {
ClassifyID int64 `json:"classifyId"`
ImageURL string `json:"imageUrl"`
Title string `json:"title"`
Level int `json:"level"`
ChildrenClassify []*GoodsClassify `json:"childrenClassify"`
}
type DeliveryType struct {
DeliveryID int64 `json:"deliveryId"`
DeliveryTypeName string `json:"deliveryTypeName"`
DeliveryType int `json:"deliveryType"`
Selected bool `json:"selected"`
}
func (a *API) QueryGoodsList(pageNum, pageSize int) (retVal []map[string]interface{}, totalCount int, err error) {
result, err := a.AccessAPI("goods/queryGoodsList", map[string]interface{}{
"pageNum": pageNum,
@@ -18,3 +71,264 @@ func (a *API) QueryGoodsList(pageNum, pageSize int) (retVal []map[string]interfa
}
return nil, 0, err
}
func (a *API) QueryCategoryTree() (retVal []*Category, err error) {
result, err := a.AccessAPI("category/queryCategoryTree", nil)
if err == nil {
categoryList := result["categoryList"].([]interface{})
retVal = make([]*Category, len(categoryList))
for k, v := range categoryList {
retVal[k] = map2Category(1, 0, v.(map[string]interface{}))
}
return retVal, nil
}
return nil, err
}
func map2Category(level int, parentID int64, mapData map[string]interface{}) *Category {
return &Category{
CategoryID: utils.MustInterface2Int64(mapData["categoryId"]),
Title: utils.Interface2String(mapData["title"]),
Level: level,
ParentID: parentID,
}
}
func (a *API) QueryChildrenCategory(categoryId int64) (retVal []*Category, err error) {
result, err := a.AccessAPI("category/queryChildrenCategory", map[string]interface{}{
"categoryId": categoryId,
})
if err == nil {
categoryList := result["categoryList"].([]interface{})
retVal = make([]*Category, len(categoryList))
for k, v := range categoryList {
retVal[k] = map2Category(2, categoryId, v.(map[string]interface{}))
}
return retVal, nil
}
return nil, err
}
func (a *API) QueryClassifyInfoList() (retVal []*GoodsClassify, err error) {
result, err := a.AccessAPI("goodsClassify/queryClassifyInfoList", nil)
if err == nil {
goodsClassifyList := interface2ClassifyList(result["goodsClassifyList"], nil)
return goodsClassifyList, nil
}
return nil, err
}
func interface2Classify(data interface{}) (clf *GoodsClassify) {
mapData, ok := data.(map[string]interface{})
if ok {
clf = &GoodsClassify{
ClassifyID: utils.MustInterface2Int64(mapData["classifyId"]),
ImageURL: utils.Interface2String(mapData["imageUrl"]),
Title: utils.Interface2String(mapData["title"]),
Level: int(utils.Interface2Int64WithDefault(mapData["level"], 0)),
ChildrenClassify: interface2ClassifyList(mapData["childrenClassify"], nil),
}
}
return clf
}
func interface2ClassifyList(data interface{}, interface2CatHandler func(data interface{}) (clf *GoodsClassify)) (clfs []*GoodsClassify) {
if interface2CatHandler == nil {
interface2CatHandler = interface2Classify
}
maps, ok := data.([]interface{})
if ok {
clfs = make([]*GoodsClassify, len(maps))
for index, v := range maps {
clfs[index] = interface2CatHandler(v)
}
}
return clfs
}
func (a *API) AddClassify(title string, parentID int64, imageURL string) (goodsClassifyID int64, err error) {
apiParams := map[string]interface{}{
"title": title,
}
if parentID > 0 {
apiParams["parentId"] = parentID
}
if imageURL != "" {
apiParams["imageUrl"] = imageURL
}
result, err := a.AccessAPI("goodsClassify/addClassify", apiParams)
if err == nil {
return utils.MustInterface2Int64(result["goodsClassifyId"]), nil
}
return 0, err
}
func (a *API) UpdateClassify(classifyID int64, title string, imageURL string) (err error) {
apiParams := map[string]interface{}{
"title": title,
"classifyId": classifyID,
}
if imageURL != "" {
apiParams["imageUrl"] = imageURL
}
_, err = a.AccessAPI("goodsClassify/updateClassify", apiParams)
return err
}
func (a *API) AddGoods(outerGoodsCode, title string, isMultiSku bool, goodsImageUrl []string, goodsDesc string, isPutAway bool, sort int, categoryId int64, classifyIdList []int64, b2cGoods *PendingSaveB2CGoodsVo, skuList []map[string]interface{}, addParams map[string]interface{}) (goodsId int64, skuMap map[string]int64, err error) {
goodsInfo := map[string]interface{}{
"outerGoodsCode": outerGoodsCode,
"title": title,
"isMultiSku": utils.Bool2Int(isMultiSku),
"goodsImageUrl": goodsImageUrl,
"goodsDesc": goodsDesc,
"isPutAway": 1 - utils.Bool2Int(isPutAway),
"sort": sort,
"categoryId": categoryId,
"b2cGoods": b2cGoods,
"skuList": skuList,
"selectedClassifyIdList": classifyIdList,
"initialSales": 100,
}
mergedMap := utils.MergeMaps(addParams, goodsInfo)
if _, ok := mergedMap["deductStockType"]; !ok {
mergedMap["deductStockType"] = DeductStockTypePay
}
result, err := a.AccessAPI("goods/addGoods", map[string]interface{}{
"goods": mergedMap,
})
if err == nil {
skuMap := make(map[string]int64)
skuList := result["skuList"].([]interface{})
for _, v := range skuList {
sku := v.(map[string]interface{})
skuMap[utils.Interface2String(sku[KeyOuterSkuCode])] = utils.MustInterface2Int64(sku[KeySkuID])
}
return utils.MustInterface2Int64(result["goodsId"]), skuMap, nil
}
return 0, nil, err
}
func (a *API) UpdateGoods(goodsID int64, title string, isMultiSku bool, goodsImageUrl []string, goodsDesc string, isPutAway bool, sort int, categoryId int64, classifyIdList []int64, b2cGoods *PendingSaveB2CGoodsVo, skuList []map[string]interface{}, addParams map[string]interface{}) (goodsId int64, skuMap map[string]int64, err error) {
goodsInfo := map[string]interface{}{
"goodsId": goodsID,
"title": title,
"isMultiSku": utils.Bool2Int(isMultiSku),
"goodsImageUrl": goodsImageUrl,
"goodsDesc": goodsDesc,
"isPutAway": 1 - utils.Bool2Int(isPutAway),
"sort": sort,
"categoryId": categoryId,
"b2cGoods": b2cGoods,
"skuList": skuList,
"selectedClassifyIdList": classifyIdList,
}
mergedMap := utils.MergeMaps(addParams, goodsInfo)
if _, ok := mergedMap["deductStockType"]; !ok {
mergedMap["deductStockType"] = DeductStockTypePay
}
result, err := a.AccessAPI("goods/updateGoods", map[string]interface{}{
"goods": mergedMap,
})
if err == nil {
skuMap := make(map[string]int64)
skuList := result["skuList"].([]interface{})
for _, v := range skuList {
sku := v.(map[string]interface{})
skuMap[utils.Interface2String(sku[KeyOuterSkuCode])] = utils.MustInterface2Int64(sku[KeySkuID])
}
return utils.MustInterface2Int64(result["goodsId"]), skuMap, nil
}
return 0, nil, err
}
func (a *API) UpdateGoodsShelfStatus(goodsIDs []int64, isPutAway bool) (err error) {
_, err = a.AccessAPI("goods/updateGoodsShelfStatus", map[string]interface{}{
"goodsIdList": goodsIDs,
"isPutAway": 1 - utils.Bool2Int(isPutAway),
})
return err
}
func (a *API) UpdateGoodsTitle(goodsID int64, title string) (err error) {
_, err = a.AccessAPI("goods/updateGoodsTitle", map[string]interface{}{
"goodsId": goodsID,
"title": title,
})
return err
}
func (a *API) FindDeliveryTypeList(goodsID int64) (retVal []*DeliveryType, err error) {
apiParams := map[string]interface{}{}
if goodsID > 0 {
apiParams["goodsId"] = goodsID
}
result, err := a.AccessAPI("goods/findDeliveryTypeList", apiParams)
if err == nil {
deliveryTypeList := result["deliveryTypeList"].([]interface{})
retVal = make([]*DeliveryType, len(deliveryTypeList))
for k, v := range deliveryTypeList {
mapData := v.(map[string]interface{})
retVal[k] = &DeliveryType{
DeliveryID: utils.MustInterface2Int64(mapData["deliveryId"]),
DeliveryType: int(utils.MustInterface2Int64(mapData["deliveryType"])),
DeliveryTypeName: utils.Interface2String(mapData["deliveryTypeName"]),
Selected: mapData["selected"].(bool),
}
}
return retVal, nil
}
return nil, err
}
func (a *API) uploadImg(imgData []byte, name string) (imgURL string, err error) {
apiParams := map[string]interface{}{
"file": imgData,
}
if name != "" {
apiParams["name"] = name
}
result, err := a.AccessAPI("goodsImage/uploadImg", apiParams)
if err == nil {
urlInfo := result["urlInfo"].([]interface{})
if len(urlInfo) > 0 {
urlInfo0 := urlInfo[0].(map[string]interface{})
if utils.MustInterface2Int64(urlInfo0["legalStatus"]) == 0 {
return utils.Interface2String(urlInfo0["url"]), nil
}
return "", fmt.Errorf("上传的图片:%s不合法", name)
}
return "", fmt.Errorf("上传的图片:%s返回为空", name)
}
return "", err
}
func (a *API) UploadImgByURL(uploadImgURL string, name string) (imgURL string, err error) {
response, err := http.Get(uploadImgURL)
if err == nil {
defer func() {
response.Body.Close()
}()
if response.StatusCode == http.StatusOK {
bodyData, err2 := ioutil.ReadAll(response.Body)
if err = err2; err == nil {
return a.uploadImg(bodyData, name)
}
} else {
err = platformapi.ErrHTTPCodeIsNot200
}
}
return "", err
}
func (a *API) FindFreightTemplateList(goodsID int64) (retVal map[string]interface{}, err error) {
apiParams := map[string]interface{}{}
if goodsID > 0 {
apiParams["goodsId"] = goodsID
}
result, err := a.AccessAPI("goods/findFreightTemplateList", apiParams)
if err == nil {
return result, nil
}
return nil, err
}

View File

@@ -15,3 +15,111 @@ func TestQueryGoodsList(t *testing.T) {
baseapi.SugarLogger.Debug(totalCount)
baseapi.SugarLogger.Debug(utils.Format4Output(result, false))
}
func TestQueryCategoryTree(t *testing.T) {
result, err := api.QueryCategoryTree()
if err != nil {
t.Fatal(err)
}
baseapi.SugarLogger.Debug(utils.Format4Output(result, false))
}
func TestQueryChildrenCategory(t *testing.T) {
result, err := api.QueryChildrenCategory(1)
if err != nil {
t.Fatal(err)
}
baseapi.SugarLogger.Debug(utils.Format4Output(result, false))
}
func TestQueryClassifyInfoList(t *testing.T) {
result, err := api.QueryClassifyInfoList()
if err != nil {
t.Fatal(err)
}
baseapi.SugarLogger.Debug(utils.Format4Output(result, false))
}
func TestAddClassify(t *testing.T) {
result, err := api.AddClassify("我的测试子分类1", 442315148, "")
if err != nil {
t.Fatal(err)
}
baseapi.SugarLogger.Debug(utils.Format4Output(result, false))
}
func TestFindDeliveryTypeList(t *testing.T) {
result, err := api.FindDeliveryTypeList(0)
if err != nil {
t.Fatal(err)
}
baseapi.SugarLogger.Debug(utils.Format4Output(result, false))
}
func TestAddGoods(t *testing.T) {
result, skuMap, err := api.AddGoods("SPU1", "测试土豆", false, []string{"https://image-c.weimobwmc.com/openruntime/8ff6950112974cff8b2f1ae1634c074d.png"}, "测试的高级土豆", true, 1, 35,
[]int64{438963248},
&PendingSaveB2CGoodsVo{
FreightTemplateId: 6537248,
DeliveryTypeIdList: []int64{177445},
B2cGoodsType: 0,
},
[]map[string]interface{}{
map[string]interface{}{
KeyOuterSkuCode: "SKU2",
KeyImageURL: "https://image-c.weimobwmc.com/openruntime/8ff6950112974cff8b2f1ae1634c074d.png",
KeySalePrice: 12.34,
KeyEditStockNum: 99999,
KeyB2cSku: &PendingSaveB2CSkuVo{
Weight: 0.12,
},
},
}, nil)
if err != nil {
t.Fatal(err)
}
baseapi.SugarLogger.Debug(utils.Format4Output(result, false))
baseapi.SugarLogger.Debug(utils.Format4Output(skuMap, false))
}
func TestUpdateGoods(t *testing.T) {
result, skuMap, err := api.UpdateGoods(20446200148, "测试土豆修改", false, []string{"https://image-c.weimobwmc.com/openruntime/8ff6950112974cff8b2f1ae1634c074d.png"}, "测试的高级土豆", true, 1, 35,
[]int64{438963248},
&PendingSaveB2CGoodsVo{
FreightTemplateId: 6537248,
DeliveryTypeIdList: []int64{177445},
B2cGoodsType: 0,
},
[]map[string]interface{}{
map[string]interface{}{
KeyOuterSkuCode: "SKU22",
KeyImageURL: "https://image-c.weimobwmc.com/openruntime/8ff6950112974cff8b2f1ae1634c074d.png",
KeySalePrice: 12.34,
KeyEditStockNum: 99999,
KeyB2cSku: &PendingSaveB2CSkuVo{
Weight: 0.12,
},
},
}, nil)
if err != nil {
t.Fatal(err)
}
baseapi.SugarLogger.Debug(utils.Format4Output(result, false))
baseapi.SugarLogger.Debug(utils.Format4Output(skuMap, false))
}
func TestUploadImgByURL(t *testing.T) {
result, err := api.UploadImgByURL("http://www.jxc4.com/static/img/logo200.png", "")
if err != nil {
t.Fatal(err)
}
baseapi.SugarLogger.Debug(utils.Format4Output(result, false))
}
func TestFindFreightTemplateList(t *testing.T) {
result, err := api.FindFreightTemplateList(0)
if err != nil {
t.Fatal(err)
}
baseapi.SugarLogger.Debug(utils.Format4Output(result, false))
}

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"sync"
@@ -20,8 +21,23 @@ const (
const (
prodURL = "https://dopen.weimob.com/api/1_0/ec"
mediaURL = "https://dopen.weimob.com/media/1_0/ec"
authURL = "https://dopen.weimob.com/fuwu/b"
accessTokenAPI = "oauth2/token"
uploadImgAPI = "goodsImage/uploadImg"
)
const (
// sku
KeySkuID = "skuId"
KeyOuterSkuCode = "outerSkuCode"
KeyImageURL = "imageUrl"
KeySalePrice = "salePrice"
KeyOriginalPrice = "originalPrice"
KeyCostPrice = "costPrice"
KeyEditStockNum = "editStockNum"
KeyB2cSku = "b2cSku"
KeySkuAttrMap = "skuAttrMap"
)
type TokenInfo struct {
@@ -100,20 +116,37 @@ func (a *API) AccessAPI(apiStr string, apiParams map[string]interface{}) (retVal
}))
request, _ = http.NewRequest(http.MethodPost, fullURL, nil)
} else {
fullURL := utils.GenerateGetURL(prodURL, apiStr, utils.Params2Map("accesstoken", a.MustGetToken().AccessToken))
var body io.Reader
if apiParams != nil {
apiParamsBytes, err := json.Marshal(apiParams)
if err != nil {
panic(fmt.Sprintf("Error when marshal %v, error:%v", apiParams, err))
if apiStr == uploadImgAPI {
fullURL := utils.GenerateGetURL(mediaURL, apiStr, utils.Params2Map("accesstoken", a.MustGetToken().AccessToken))
var b bytes.Buffer
w := multipart.NewWriter(&b)
imgData := apiParams["file"].([]byte)
imgName, _ := apiParams["name"].(string)
if fw, err := w.CreateFormFile("file", imgName); err != nil {
panic(err.Error())
} else {
fw.Write(imgData)
}
body = bytes.NewReader(apiParamsBytes)
w.Close()
// b.WriteString(utils.Map2URLValues(params).Encode())
request, _ = http.NewRequest(http.MethodPost, fullURL, &b)
request.Header.Set("Content-Type", w.FormDataContentType())
} else {
fullURL := utils.GenerateGetURL(prodURL, apiStr, utils.Params2Map("accesstoken", a.MustGetToken().AccessToken))
var body io.Reader
if apiParams != nil {
apiParamsBytes, err := json.Marshal(apiParams)
if err != nil {
panic(fmt.Sprintf("Error when marshal %v, error:%v", apiParams, err))
}
body = bytes.NewReader(apiParamsBytes)
}
request, _ = http.NewRequest(http.MethodPost, fullURL, body)
request.Header.Set("Content-Type", "application/json; charset=utf-8")
request.Header.Set("Content-Encoding", "gzip, deflate")
request.Header.Set("User-Agent", "weimob-golang-api")
// request.Close = true //todo 为了性能考虑还是不要关闭
}
request, _ = http.NewRequest(http.MethodPost, fullURL, body)
request.Header.Set("Content-Type", "application/json; charset=utf-8")
request.Header.Set("Content-Encoding", "gzip, deflate")
request.Header.Set("User-Agent", "weimob-golang-api")
// request.Close = true //todo 为了性能考虑还是不要关闭
}
return request
},

View File

@@ -19,16 +19,9 @@ func init() {
baseapi.Init(sugarLogger)
tokenStr := `
{
"token_type": "bearer",
"access_token": "db2f4a02-2097-4636-8c8a-cc5576f402cf",
"expires_in": 7199,
"refresh_token": "7b961b6b-0dc4-43e1-8c38-a9a10e2e130d",
"refresh_token_expires_in": 604799,
"scope": "default",
"public_account_id": "100000386048",
"business_id": "1224609670"
}`
{"token_type":"bearer","access_token":"4c170b37-e70b-4fc0-aa03-d02fc1ebf43d","expires_in":7199,"refresh_token":"7b961b6b-0dc4-43e1-8c38-a9a10e2e130d","refresh_token_expires_in":607932,"scope":"default","public_account_id":"100000386048","business_id":"1224609670"}
`
var token *TokenInfo
if err := utils.UnmarshalUseNumber([]byte(tokenStr), &token); err != nil {
panic(err)

View File

@@ -190,6 +190,13 @@ func Bool2String(value bool) string {
return "false"
}
func Bool2Int(value bool) int {
if value {
return 1
}
return 0
}
func Str2Int64WithDefault(str string, defValue int64) int64 {
retVal, err := strconv.ParseInt(str, 10, 64)
if err != nil {