From 684a104a25d16f33fb73b76065481c200d5b303a Mon Sep 17 00:00:00 2001 From: gazebo Date: Thu, 22 Nov 2018 10:21:16 +0800 Subject: [PATCH] - mtwmapi --- platformapi/jdapi/sku_test.go | 7 + platformapi/mtwmapi/food.go | 147 +++++++++++++++++++ platformapi/mtwmapi/food_test.go | 16 +++ platformapi/mtwmapi/mtwmapi.go | 143 ++++++++++++++++++ platformapi/mtwmapi/mtwmapi_test.go | 30 ++++ platformapi/mtwmapi/order.go | 125 ++++++++++++++++ platformapi/mtwmapi/order_test.go | 13 ++ platformapi/mtwmapi/poi.go | 135 +++++++++++++++++ platformapi/mtwmapi/poi_test.go | 71 +++++++++ platformapi/mtwmapi/retail.go | 207 +++++++++++++++++++++++++++ platformapi/mtwmapi/retail_test.go | 69 +++++++++ platformapi/mtwmapi/shipping.go | 42 ++++++ platformapi/mtwmapi/shipping_test.go | 30 ++++ utils/utils.go | 9 ++ 14 files changed, 1044 insertions(+) create mode 100644 platformapi/mtwmapi/food.go create mode 100644 platformapi/mtwmapi/food_test.go create mode 100644 platformapi/mtwmapi/mtwmapi.go create mode 100644 platformapi/mtwmapi/mtwmapi_test.go create mode 100644 platformapi/mtwmapi/order.go create mode 100644 platformapi/mtwmapi/order_test.go create mode 100644 platformapi/mtwmapi/poi.go create mode 100644 platformapi/mtwmapi/poi_test.go create mode 100644 platformapi/mtwmapi/retail.go create mode 100644 platformapi/mtwmapi/retail_test.go create mode 100644 platformapi/mtwmapi/shipping.go create mode 100644 platformapi/mtwmapi/shipping_test.go diff --git a/platformapi/jdapi/sku_test.go b/platformapi/jdapi/sku_test.go index 27e44ff4..fb43f687 100644 --- a/platformapi/jdapi/sku_test.go +++ b/platformapi/jdapi/sku_test.go @@ -124,3 +124,10 @@ func TestGetProductStatust(t *testing.T) { t.Fatal("GetProductStatus should return error") } } + +func TestDelShopCategory(t *testing.T) { + err := jdapi.DelShopCategory(4784689) + if err != nil { + t.Fatal(err) + } +} diff --git a/platformapi/mtwmapi/food.go b/platformapi/mtwmapi/food.go new file mode 100644 index 00000000..3c640276 --- /dev/null +++ b/platformapi/mtwmapi/food.go @@ -0,0 +1,147 @@ +package mtwmapi + +import ( + "time" + + "git.rosy.net.cn/baseapi/utils" +) + +type FoodCategoryInfo struct { + Name string `json:"name"` + Sequence int `json:"sequence"` + CTime time.Time `json:"ctime"` + UTime time.Time `json:"utime"` +} + +func (a *API) FoodCatUpdate(poiCode, originName, name string, sequence int) (err error) { + params := map[string]interface{}{ + KeyAppPoiCode: poiCode, + "category_name_origin": originName, + "sequence": sequence, + } + if name != "" { + params["category_name"] = name + } + _, err = a.AccessAPI("foodCat/update", false, params) + return err +} + +func (a *API) FoodCatDelete(poiCode, name string) (err error) { + _, err = a.AccessAPI("foodCat/delete", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + "category_name": name, + }) + return err +} + +func (a *API) FoodInitData(poiCode, foodCode string, params map[string]interface{}) (err error) { + _, err = a.AccessAPI("food/initdata", false, utils.MergeMaps(map[string]interface{}{ + KeyAppPoiCode: poiCode, + KeyAppFoodCode: foodCode, + }, params)) + return err +} + +func (a *API) FoodDelete(poiCode, foodCode string) (err error) { + _, err = a.AccessAPI("food/delete", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + KeyAppFoodCode: foodCode, + }) + return err +} + +// offset 从0开始,limit最大不能超过200 +func (a *API) FoodList(poiCode string, offset, limit int) (foodList []map[string]interface{}, err error) { + result, err := a.AccessAPI("food/list", true, map[string]interface{}{ + KeyAppPoiCode: poiCode, + "offset": offset, + "limit": limit, + }) + if err == nil { + return utils.Slice2MapSlice(result.([]interface{})), nil + } + return nil, err +} + +func (a *API) FoodBatchInitData(poiCode string, foodData map[string]interface{}) (err error) { + _, err = a.AccessAPI("food/batchinitdata", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + "food_data": foodData, + }) + return err +} + +func (a *API) FoodCatList(poiCode string) (foodCatList []*FoodCategoryInfo, err error) { + result, err := a.AccessAPI("foodCat/list", true, map[string]interface{}{ + KeyAppPoiCode: poiCode, + }) + if err == nil { + catListTmp := result.([]interface{}) + foodCatList = make([]*FoodCategoryInfo, len(catListTmp)) + for k, v := range catListTmp { + cat := v.(map[string]interface{}) + foodCatList[k] = &FoodCategoryInfo{ + Name: utils.Interface2String(cat["name"]), + Sequence: int(utils.MustInterface2Int64(cat["sequence"])), + CTime: time.Unix(utils.MustInterface2Int64(cat["ctime"]), 0), + UTime: time.Unix(utils.MustInterface2Int64(cat["utime"]), 0), + } + } + return foodCatList, nil + } + return nil, err +} + +func (a *API) FoodSkuSave(poiCode, foodCode string, skus []map[string]interface{}) (err error) { + _, err = a.AccessAPI("food/sku/save", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + KeyAppFoodCode: foodCode, + "skus": skus, + }) + return err +} + +func (a *API) FoodSkuDelete(poiCode, foodCode, skuID string) (err error) { + _, err = a.AccessAPI("food/sku/delete", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + KeyAppFoodCode: foodCode, + "sku_id": skuID, + }) + return err +} + +func (a *API) FoodSkuPrice(poiCode string, foodData []map[string]interface{}) (err error) { + _, err = a.AccessAPI("food/sku/price", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + "food_data": foodData, + }) + return err +} + +func (a *API) FoodSkuStock(poiCode string, foodData []map[string]interface{}) (err error) { + _, err = a.AccessAPI("food/sku/stock", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + "food_data": foodData, + }) + return err +} + +func (a *API) FoodGet(poiCode, foodCode string) (food map[string]interface{}, err error) { + result, err := a.AccessAPI("food/get", true, map[string]interface{}{ + KeyAppPoiCode: poiCode, + KeyAppFoodCode: foodCode, + }) + if err == nil { + return result.(map[string]interface{}), nil + } + return nil, err +} + +func (a *API) FoodSkuSellStatus(poiCode string, foodData []map[string]interface{}, status int) (err error) { + _, err = a.AccessAPI("food/sku/sellStatus", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + "food_data": foodData, + "status": status, + }) + return err +} diff --git a/platformapi/mtwmapi/food_test.go b/platformapi/mtwmapi/food_test.go new file mode 100644 index 00000000..a10aca7c --- /dev/null +++ b/platformapi/mtwmapi/food_test.go @@ -0,0 +1,16 @@ +package mtwmapi + +import ( + "testing" +) + +func TestFoodList(t *testing.T) { + result, err := api.FoodList(testPoiCode, 0, 200) + if err != nil { + t.Fatal(err) + } + if len(result) == 0 { + t.Fatal("should have food") + } + // t.Log(result) +} diff --git a/platformapi/mtwmapi/mtwmapi.go b/platformapi/mtwmapi/mtwmapi.go new file mode 100644 index 00000000..1e26541c --- /dev/null +++ b/platformapi/mtwmapi/mtwmapi.go @@ -0,0 +1,143 @@ +package mtwmapi + +import ( + "bytes" + "crypto/md5" + "fmt" + "mime/multipart" + "net/http" + "net/url" + "sort" + "strings" + "time" + + "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 ( + GeneralMaxLimit = 200 // 大多数的API的批处理最大条数 +) + +type API struct { + appID string + secret string + client *http.Client + config *platformapi.APIConfig +} + +func New(appID, secret string, config ...*platformapi.APIConfig) *API { + curConfig := platformapi.DefAPIConfig + if len(config) > 0 { + curConfig = *config[0] + } + return &API{ + appID: appID, + secret: secret, + client: &http.Client{Timeout: curConfig.ClientTimeout}, + config: &curConfig, + } +} + +func (a *API) genURL(cmd string) string { + return apiURL + "/" + cmd +} + +func (a *API) signParams(cmd 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 := a.genURL(cmd) + "?" + 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) AccessAPI(cmd string, isGet bool, bizParams map[string]interface{}) (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) + } + params[signKey] = a.signParams(cmd, 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") + } + // request.Close = true //todo 为了性能考虑还是不要关闭 + return request + }, + a.config, + func(response *http.Response) (errLevel string, err error) { + jsonResult1, err := utils.HTTPResponse2Json(response) + // baseapi.SugarLogger.Debug(utils.Format4Output(jsonResult1, false)) + if err != nil { + return platformapi.ErrLevelGeneralFail, platformapi.ErrResponseDataFormatWrong + } + if _, ok := jsonResult1["error"]; ok { + errorInfo := jsonResult1["error"].(map[string]interface{}) + newErr := utils.NewErrorIntCode(errorInfo["msg"].(string), int(utils.MustInterface2Int64(errorInfo["code"]))) + return platformapi.ErrLevelCodeIsNotOK, newErr + } + retVal = jsonResult1["data"] + return platformapi.ErrLevelSuccess, nil + }) + return retVal, err +} diff --git a/platformapi/mtwmapi/mtwmapi_test.go b/platformapi/mtwmapi/mtwmapi_test.go new file mode 100644 index 00000000..06e30817 --- /dev/null +++ b/platformapi/mtwmapi/mtwmapi_test.go @@ -0,0 +1,30 @@ +package mtwmapi + +import ( + "testing" + + "git.rosy.net.cn/baseapi" + "git.rosy.net.cn/baseapi/utils" + "go.uber.org/zap" +) + +var ( + api *API + sugarLogger *zap.SugaredLogger +) + +func init() { + logger, _ := zap.NewDevelopment() + sugarLogger = logger.Sugar() + baseapi.Init(sugarLogger) + + api = New("589", "a81eb3df418d83d6a1a4b7c572156d2f") +} + +func TestAccessAPI(t *testing.T) { + result, err := api.AccessAPI("poi/getids", true, nil) + if err != nil { + t.Fatal(err) + } + t.Log(utils.Format4Output(result, false)) +} diff --git a/platformapi/mtwmapi/order.go b/platformapi/mtwmapi/order.go new file mode 100644 index 00000000..f8792979 --- /dev/null +++ b/platformapi/mtwmapi/order.go @@ -0,0 +1,125 @@ +package mtwmapi + +import ( + "git.rosy.net.cn/baseapi/utils" +) + +const ( + MaxBatchPullPhoneNumberLimit = 1000 +) + +func (a *API) OrderReceived(orderID int64) (err error) { + _, err = a.AccessAPI("order/poi_received", true, map[string]interface{}{ + KeyOrderID: orderID, + }) + return err +} + +func (a *API) OrderConfirm(orderID int64) (err error) { + _, err = a.AccessAPI("order/confirm", true, map[string]interface{}{ + KeyOrderID: orderID, + }) + return err +} + +func (a *API) OrderCancel(orderID int64) (err error) { + _, err = a.AccessAPI("order/cancel", true, map[string]interface{}{ + KeyOrderID: orderID, + }) + return err +} + +func (a *API) OrderDelivering(orderID int64) (err error) { + _, err = a.AccessAPI("order/delivering", true, map[string]interface{}{ + KeyOrderID: orderID, + }) + return err +} + +func (a *API) OrderArrived(orderID int64) (err error) { + _, err = a.AccessAPI("order/arrived", true, map[string]interface{}{ + KeyOrderID: orderID, + }) + return err +} + +func (a *API) OrderRefundAgree(orderID int64, reason string) (err error) { + _, err = a.AccessAPI("order/refund/agree", true, map[string]interface{}{ + KeyOrderID: orderID, + "reason": reason, + }) + return err +} + +func (a *API) OrderRefundReject(orderID int64, reason string) (err error) { + _, err = a.AccessAPI("order/refund/reject", true, map[string]interface{}{ + KeyOrderID: orderID, + "reason": reason, + }) + return err +} + +func (a *API) OrderViewStatus(orderID int64) (status int, err error) { + result, err := a.AccessAPI("order/viewstatus", true, map[string]interface{}{ + KeyOrderID: orderID, + }) + if err == nil { + // baseapi.SugarLogger.Debug(result) + return int(utils.MustInterface2Int64(result.(map[string]interface{})["status"])), nil + } + return 0, err +} + +func (a *API) OrderGetOrderDetail(orderID int64, isMTLogistics bool) (orderInfo map[string]interface{}, err error) { + params := map[string]interface{}{ + KeyOrderID: orderID, + } + if isMTLogistics { + params["is_mt_logistics"] = 1 + } + result, err := a.AccessAPI("order/getOrderDetail", true, params) + if err == nil { + return result.(map[string]interface{}), nil + } + return nil, err +} + +func (a *API) OrderLogisticsPush(orderID int64, reason string) (err error) { + _, err = a.AccessAPI("order/logistics/push", true, map[string]interface{}{ + KeyOrderID: orderID, + }) + return err +} + +func (a *API) OrderLogisticsCancel(orderID int64, reason string) (err error) { + _, err = a.AccessAPI("order/logistics/cancel", true, map[string]interface{}{ + KeyOrderID: orderID, + }) + return err +} + +func (a *API) OrderLogisticsStatus(orderID int64) (status map[string]interface{}, err error) { + result, err := a.AccessAPI("order/logistics/status", true, map[string]interface{}{ + KeyOrderID: orderID, + }) + if err == nil { + return result.(map[string]interface{}), nil + } + return nil, err +} + +// limit最大为MaxBatchPullPhoneNumberLimit = 1000 +func (a *API) OrderBatchPullPhoneNumber(poiCode string, offset, limit int) (realNumberList []map[string]interface{}, err error) { + params := map[string]interface{}{ + "offset": offset, + "limit": limit, + } + if poiCode != "" { + params[KeyAppPoiCode] = poiCode + } + result, err := a.AccessAPI("order/batchPullPhoneNumber", false, params) + if err == nil { + return utils.Slice2MapSlice(result.([]interface{})), nil + } + return nil, err +} diff --git a/platformapi/mtwmapi/order_test.go b/platformapi/mtwmapi/order_test.go new file mode 100644 index 00000000..2b77815c --- /dev/null +++ b/platformapi/mtwmapi/order_test.go @@ -0,0 +1,13 @@ +package mtwmapi + +import ( + "testing" +) + +func TestOrderViewStatus(t *testing.T) { + result, err := api.OrderViewStatus(9) + if err != nil { + t.Fatal(err) + } + t.Log(result) +} diff --git a/platformapi/mtwmapi/poi.go b/platformapi/mtwmapi/poi.go new file mode 100644 index 00000000..b7a352c3 --- /dev/null +++ b/platformapi/mtwmapi/poi.go @@ -0,0 +1,135 @@ +package mtwmapi + +import ( + "io/ioutil" + "net/http" + "strings" + + "git.rosy.net.cn/baseapi/platformapi" + "git.rosy.net.cn/baseapi/utils" +) + +type PoiCategoryInfo struct { + ID int `json:"id"` + Name string `json:"name"` +} + +func (a *API) PoiSave(poiCode string, poiParams map[string]interface{}) (err error) { + _, err = a.AccessAPI("poi/save", false, utils.MergeMaps(utils.Params2Map(KeyAppPoiCode, poiCode), poiParams)) + return err +} + +func (a *API) PoiGetIDs() (ids []string, err error) { + result, err := a.AccessAPI("poi/getids", true, nil) + if err == nil { + idsTmp := result.([]interface{}) + ids = make([]string, len(idsTmp)) + for k, v := range idsTmp { + ids[k] = v.(string) + } + return ids, nil + } + return nil, err +} + +func (a *API) PoiMGet(poiCodes []string) (pois []map[string]interface{}, err error) { + result, err := a.AccessAPI("poi/mget", true, map[string]interface{}{ + KeyAppPoiCodes: strings.Join(poiCodes, ","), + }) + if err == nil { + return utils.Slice2MapSlice(result.([]interface{})), nil + } + return nil, err +} + +func (a *API) PoiGet(poiCode string) (poi map[string]interface{}, err error) { + result, err := a.PoiMGet([]string{poiCode}) + if err == nil { + return result[0], nil + } + return nil, err +} + +func (a *API) PoiClose(poiCode string) (err error) { + _, err = a.AccessAPI("poi/close", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + }) + return err +} + +func (a *API) PoiOnline(poiCode string) (err error) { + _, err = a.AccessAPI("poi/online", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + }) + return err +} + +func (a *API) PoiUpdatePromoteInfo(poiCode, promotionInfo string) (err error) { + _, err = a.AccessAPI("poi/updatepromoteinfo", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + "promotion_info": promotionInfo, + }) + return err +} + +func (a *API) PoiTagList(poiCode string) (catList []*PoiCategoryInfo, err error) { + result, err := a.AccessAPI("poiTag/list", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + }) + if err == nil { + catListTmp := result.([]interface{}) + catList = make([]*PoiCategoryInfo, len(catListTmp)) + for k, v := range catListTmp { + cat := v.(map[string]interface{}) + catList[k] = &PoiCategoryInfo{ + ID: int(utils.MustInterface2Int64(cat["id"])), + Name: utils.Interface2String(cat["name"]), + } + } + return catList, nil + } + return nil, err +} + +func (a *API) PoiShipTimeUpdate(poiCode string, beginTime1, endTime1, beginTime2, endTime2 string) (err error) { + shippingTime := beginTime1 + "-" + endTime1 + if beginTime2 != "" { + shippingTime += "," + beginTime2 + "-" + endTime2 + } + _, err = a.AccessAPI("poi/updatepromoteinfo", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + "shipping_time": shippingTime, + }) + return err +} + +// 美团要求必须是jpg|jpeg图片格式,且imgName必须以jpg或jpeg结尾 +func (a *API) ImageUpload(poiCode, imgName string, imgData []byte) (imgID string, err error) { + result, err := a.AccessAPI("image/upload", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + KeyImgName: imgName, + KeyImgData: imgData, + }) + if err == nil { + return result.(string), nil + } + return "", err +} + +func (a *API) ImageUploadByURL(poiCode, imgName, imgURL string) (imgID string, err error) { + response, err := http.Get(imgURL) + 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.ImageUpload(poiCode, imgName, bodyData) + } + } else { + err = platformapi.ErrHTTPCodeIsNot200 + } + } + return "", err +} diff --git a/platformapi/mtwmapi/poi_test.go b/platformapi/mtwmapi/poi_test.go new file mode 100644 index 00000000..6eae3745 --- /dev/null +++ b/platformapi/mtwmapi/poi_test.go @@ -0,0 +1,71 @@ +package mtwmapi + +import ( + "testing" + + "git.rosy.net.cn/baseapi/utils" +) + +const ( + testPoiCode = "test_poi_01" +) + +func TestPoiGetIDs(t *testing.T) { + result, err := api.PoiGetIDs() + if err != nil { + t.Fatal(err) + } + if len(result) == 0 { + t.Fatal("should have ids") + } + // t.Log(result) +} + +func TestPoiMGet(t *testing.T) { + result, err := api.PoiMGet([]string{testPoiCode}) + if err != nil { + t.Fatal(err) + } + if len(result) != 1 { + t.Fatal("result len is not ok") + } + if result[0][KeyAppPoiCode] != testPoiCode { + t.Fatal("test_poi_01 is not equal") + } + t.Log(utils.Format4Output(result, false)) +} + +func TestPoiSave(t *testing.T) { + result, err := api.PoiMGet([]string{testPoiCode}) + if err != nil { + t.Fatal(err) + } + poiParams := utils.FilterMapNilMembers(result[0]) + poiParams["name"] = "中国" + err = api.PoiSave(testPoiCode, poiParams) + if err != nil { + t.Fatal(err) + } +} + +func TestPoiTagList(t *testing.T) { + result, err := api.PoiTagList(testPoiCode) + if err != nil { + t.Fatal(err) + } + if len(result) == 0 { + t.Fatal("should have cats") + } + t.Log(utils.Format4Output(result, false)) +} + +func TestImageUploadByURL(t *testing.T) { + result, err := api.ImageUploadByURL(testPoiCode, "hello.jpg", "http://image.jxc4.com/56cd1e88ada3660f37548c2d29ea2158.jpg") + if err != nil { + t.Fatal(err) + } + if len(result) == 0 { + t.Fatal("should have result") + } + t.Log(result) +} diff --git a/platformapi/mtwmapi/retail.go b/platformapi/mtwmapi/retail.go new file mode 100644 index 00000000..22ff4798 --- /dev/null +++ b/platformapi/mtwmapi/retail.go @@ -0,0 +1,207 @@ +package mtwmapi + +import ( + "strings" + + "git.rosy.net.cn/baseapi/utils" +) + +type RetailCategoryInfo struct { + Name string `json:"name"` + Sequence int `json:"sequence"` + Level int `json:"level"` + Children []*RetailCategoryInfo `json:"children"` +} + +// 美团分类没有ID,就以名字为唯一标识,不论级别都必须不能重名 +// name(和originName)的长度不能超过10个字符(字符,不是字节) +// 首次创建分类时,如果secondaryName不为空,会把一级(如果不存在)及二级分类创建起来, +// 如果一级分类已经存在时,要创建二级分类,则要求originName与name都不为空,且值是一样的 +// 大概逻辑是: +// 如果originName不存在,则是创建一级分类,如果secondaryName相应还要创建二级分类,此时的sequence指的是二级的,一级的sequence就没有指定了 +// 如果originName存在,则指的是更新,一级分类就必须存在,如果不想改名,将name填成与originName一样的即可 +// todo 但这个函数好像就无法实现变更二级分类的名字及sequence +func (a *API) RetailCatUpdate(poiCode, originName, name, secondaryName string, sequence int) (err error) { + params := map[string]interface{}{ + KeyAppPoiCode: poiCode, + "category_name": name, + "sequence": sequence, + } + if originName != "" { + params["category_name_origin"] = originName + } + if secondaryName != "" { + params["secondary_category_name"] = secondaryName + } + _, err = a.AccessAPI("retailCat/update", false, params) + return err +} + +func (a *API) RetailCatDelete(poiCode, name string) (err error) { + _, err = a.AccessAPI("retailCat/delete", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + "category_name": name, + }) + return err +} + +func (a *API) RetailCatList(poiCode string) (retailCatList []*RetailCategoryInfo, err error) { + result, err := a.AccessAPI("retailCat/list", true, map[string]interface{}{ + KeyAppPoiCode: poiCode, + }) + if err == nil { + return interface2CatList(result, 1, nil), nil + } + return nil, err +} + +func (a *API) RetailInitData(poiCode, foodCode string, params map[string]interface{}) (err error) { + _, err = a.AccessAPI("retail/initdata", false, utils.MergeMaps(map[string]interface{}{ + KeyAppPoiCode: poiCode, + KeyAppFoodCode: foodCode, + }, params)) + return err +} + +// offset 从0开始,limit最大不能超过200 +func (a *API) RetailList(poiCode string, offset, limit int) (foodList []map[string]interface{}, err error) { + result, err := a.AccessAPI("retail/list", true, map[string]interface{}{ + KeyAppPoiCode: poiCode, + "offset": offset, + "limit": limit, + }) + if err == nil { + return utils.Slice2MapSlice(result.([]interface{})), nil + } + return nil, err +} + +func (a *API) RetailBatchInitData(poiCode string, foodData map[string]interface{}) (err error) { + _, err = a.AccessAPI("retail/batchinitdata", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + "food_data": string(utils.MustMarshal(foodData)), + }) + return err +} + +func (a *API) RetailSkuPrice(poiCode string, foodData []map[string]interface{}) (err error) { + _, err = a.AccessAPI("retail/sku/price", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + "food_data": foodData, + }) + return err +} + +func (a *API) RetailSkuStock(poiCode string, foodData []map[string]interface{}) (err error) { + _, err = a.AccessAPI("retail/sku/stock", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + "food_data": foodData, + }) + return err +} + +func (a *API) RetailGet(poiCode, foodCode string) (food map[string]interface{}, err error) { + result, err := a.AccessAPI("retail/get", true, map[string]interface{}{ + KeyAppPoiCode: poiCode, + KeyAppFoodCode: foodCode, + }) + if err == nil { + return result.(map[string]interface{}), nil + } + return nil, err +} + +func (a *API) RetailSkuSave(poiCode, foodCode string, standardSkus, unstandardSkus []map[string]interface{}) (err error) { + _, err = a.AccessAPI("retail/sku/save", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + KeyAppFoodCode: foodCode, + "standard_skus": standardSkus, + "unstandard_skus": unstandardSkus, + }) + return err +} + +func (a *API) RetailSkuSellStatus(poiCode string, foodData []map[string]interface{}, status int) (err error) { + _, err = a.AccessAPI("retail/sku/sellStatus", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + "food_data": foodData, + "status": status, + }) + return err +} + +func (a *API) RetailDelete(poiCode, foodCode string) (err error) { + _, err = a.AccessAPI("retail/delete", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + KeyAppFoodCode: foodCode, + }) + return err +} + +func (a *API) RetailSkuDelete(poiCode, foodCode, skuID string) (err error) { + _, err = a.AccessAPI("retail/sku/delete", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + KeyAppFoodCode: foodCode, + "sku_id": skuID, + }) + return err +} + +// 就是厂商商品类别 +func (a *API) RetailGetSpTagIds() (tagIds []map[string]interface{}, err error) { + result, err := a.AccessAPI("retail/getSpTagIds", true, nil) + if err == nil { + return utils.Slice2MapSlice(result.([]interface{})), nil + } + return nil, err +} + +// 此接口将申请授权后方可接入 +func (a *API) RetailCatSkuBatchDelete(poiCode string, catNames []string, secondaryCatNames []string, foodCodes []string) (err error) { + params := map[string]interface{}{ + KeyAppPoiCode: poiCode, + } + if len(catNames) > 0 { + params["category_names"] = strings.Join(catNames, ",") + } + if len(secondaryCatNames) > 0 { + params["secondary_category_names"] = strings.Join(secondaryCatNames, ",") + } + if len(foodCodes) > 0 { + params["app_food_codes"] = strings.Join(foodCodes, ",") + } + _, err = a.AccessAPI("retailCat/batchdelete/catandretail", false, params) + return err +} + +////////////////////// +// 私有辅助函数 +func interface2Cat(data interface{}, level int) (cat *RetailCategoryInfo) { + mapData, ok := data.(map[string]interface{}) + if ok { + cat = &RetailCategoryInfo{ + Name: utils.Interface2String(mapData["name"]), + Sequence: int(utils.Interface2Int64WithDefault(mapData["sequence"], 0)), + Level: level, + } + children := mapData["children"] + if children != nil { + cat.Children = interface2CatList(children, level+1, nil) + } + } + return cat +} + +func interface2CatList(data interface{}, level int, interface2CatHandler func(data interface{}, level int) (cat *RetailCategoryInfo)) (cats []*RetailCategoryInfo) { + if interface2CatHandler == nil { + interface2CatHandler = interface2Cat + } + maps, ok := data.([]interface{}) + if ok { + cats = make([]*RetailCategoryInfo, len(maps)) + for index, v := range maps { + cats[index] = interface2CatHandler(v, level) + } + } + return cats +} diff --git a/platformapi/mtwmapi/retail_test.go b/platformapi/mtwmapi/retail_test.go new file mode 100644 index 00000000..34d9a4f8 --- /dev/null +++ b/platformapi/mtwmapi/retail_test.go @@ -0,0 +1,69 @@ +package mtwmapi + +import ( + "testing" + + "git.rosy.net.cn/baseapi/utils" +) + +func TestRetailCatList(t *testing.T) { + result, err := api.RetailCatList(testPoiCode) + if err != nil { + t.Fatal(err) + } + t.Log(utils.Format4Output(result, false)) +} + +func TestRetailCatUpdate(t *testing.T) { + // err := api.RetailCatUpdate(testPoiCode, "测试一级类别", "测试一级类别2", "", 2) + err := api.RetailCatUpdate(testPoiCode, "测试一级类别", "测试一级类别", "", 2) + if err != nil { + t.Fatal(err) + } +} +func TestRetailCatDelete(t *testing.T) { + var err error + err = api.RetailCatDelete(testPoiCode, utils.GetUpperUUID()) + if err == nil { + t.Fatal("should return error that can not find such cat") + } + uniqueCatName := "一二三四五六七八九十" + // uniqueCatName := "1234567890" //fmt.Sprintf("CAT%d", time.Now().Unix()) + err = api.RetailCatUpdate(testPoiCode, "", uniqueCatName, "", 15) + if err != nil { + t.Fatal(err) + } + err = api.RetailCatDelete(testPoiCode, uniqueCatName) + if err != nil { + t.Fatal(err) + } +} + +func TestRetailList(t *testing.T) { + result, err := api.RetailList(testPoiCode, 0, GeneralMaxLimit) + if err != nil { + t.Fatal(err) + } + if len(result) == 0 { + t.Fatal("should have items") + } + // t.Log(utils.Format4Output(result, false)) +} + +func TestRetailGetSpTagIds(t *testing.T) { + result, err := api.RetailGetSpTagIds() + if err != nil { + t.Fatal(err) + } + if len(result) == 0 { + t.Fatal("should have items") + } + // t.Log(utils.Format4Output(result, false)) +} + +func TestRetailCatSkuBatchDelete(t *testing.T) { + err := api.RetailCatSkuBatchDelete(testPoiCode, []string{"测试一级类别"}, nil, nil) + if err != nil { + t.Fatal(err) + } +} diff --git a/platformapi/mtwmapi/shipping.go b/platformapi/mtwmapi/shipping.go new file mode 100644 index 00000000..7adf2cc8 --- /dev/null +++ b/platformapi/mtwmapi/shipping.go @@ -0,0 +1,42 @@ +package mtwmapi + +import "git.rosy.net.cn/baseapi/utils" + +const ( + PoiOpenLevelNormal = 1 + PoiOpenLevelHaveRest = 3 + PoiOnline = 1 + PoiOffline = 0 +) + +func (a *API) ShippingSave(poiCode string, area string, minPrice, shippingFee float32) (err error) { + _, err = a.AccessAPI("shipping/save", false, map[string]interface{}{ + KeyAppPoiCode: poiCode, + "app_shipping_code": 1, + "type": 1, + "area": area, + "min_price": minPrice, + "shipping_fee": shippingFee, + }) + return err +} + +func (a *API) ShippingList(poiCode string) (shippingList []map[string]interface{}, err error) { + result, err := a.AccessAPI("shipping/list", true, map[string]interface{}{ + KeyAppPoiCode: poiCode, + }) + if err == nil { + return utils.Slice2MapSlice(result.([]interface{})), nil + } + return nil, err +} + +func (a *API) ShippingFetch(poiCode string) (shippingList []map[string]interface{}, err error) { + result, err := a.AccessAPI("shipping/fetch", true, map[string]interface{}{ + KeyAppPoiCode: poiCode, + }) + if err == nil { + return utils.Slice2MapSlice(result.([]interface{})), nil + } + return nil, err +} diff --git a/platformapi/mtwmapi/shipping_test.go b/platformapi/mtwmapi/shipping_test.go new file mode 100644 index 00000000..a60236d9 --- /dev/null +++ b/platformapi/mtwmapi/shipping_test.go @@ -0,0 +1,30 @@ +package mtwmapi + +import ( + "testing" + + "git.rosy.net.cn/baseapi/utils" +) + +func TestShippingSave(t *testing.T) { + err := api.ShippingSave(testPoiCode, `[{"x":39941199,"y":116385384},{"x":39926983,"y":116361694},{"x":39921586,"y":116398430}]`, 0, 0) + if err != nil { + t.Fatal(err) + } +} + +func TestShippingList(t *testing.T) { + result, err := api.ShippingList(testPoiCode) + if err != nil { + t.Fatal(err) + } + t.Log(utils.Format4Output(result, false)) +} + +func TestShippingFetch(t *testing.T) { + result, err := api.ShippingFetch(testPoiCode) + if err != nil { + t.Fatal(err) + } + t.Log(utils.Format4Output(result, false)) +} diff --git a/utils/utils.go b/utils/utils.go index 7c2bddc7..bb8964bf 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -215,3 +215,12 @@ func RemoveGeneralMapKeys(obj map[string]interface{}, keys ...string) map[string } return obj } + +func FilterMapNilMembers(mapData map[string]interface{}) map[string]interface{} { + for k, v := range mapData { + if v == nil { + delete(mapData, k) + } + } + return mapData +}