326 lines
11 KiB
Go
326 lines
11 KiB
Go
package mtwmapi
|
||
|
||
import (
|
||
"strings"
|
||
|
||
"git.rosy.net.cn/baseapi/utils"
|
||
)
|
||
|
||
const (
|
||
MaxSkuNameCharCount = 30 // SkuName的最大字符数
|
||
|
||
MaxStoreSkuBatchSize = 200 // retail/sku/stock, retail/sku/sellStatus和retail/sku/price这些批量操作的最大值
|
||
MaxRetailListPageSize = 200
|
||
MaxBatchDeleteSize = 100 // retailCat/batchdelete/catandretail这个接口的批量最大值
|
||
)
|
||
|
||
type RetailCategoryInfo struct {
|
||
Name string `json:"name"`
|
||
Sequence int `json:"sequence"`
|
||
Level int `json:"level"`
|
||
Children []*RetailCategoryInfo `json:"children"`
|
||
}
|
||
|
||
type RetailTag struct {
|
||
ID int64 `json:"id"`
|
||
Name string `json:"name"`
|
||
Level int `json:"level"`
|
||
NamePath string `json:"namePath"`
|
||
}
|
||
|
||
type BareStoreSkuInfo struct {
|
||
SkuID string `json:"sku_id"`
|
||
Price string `json:"price,omitempty"`
|
||
Stock string `json:"stock,omitempty"`
|
||
}
|
||
|
||
type BareStoreFoodInfo struct {
|
||
AppFoodCode string `json:"app_food_code"`
|
||
Skus []*BareStoreSkuInfo `json:"skus"`
|
||
}
|
||
|
||
type AvailableTimesInfo struct {
|
||
Friday string `json:"friday"`
|
||
Monday string `json:"monday"`
|
||
Saturday string `json:"saturday"`
|
||
Sunday string `json:"sunday"`
|
||
Thursday string `json:"thursday"`
|
||
Tuesday string `json:"tuesday"`
|
||
Wednesday string `json:"wednesday"`
|
||
}
|
||
|
||
type SkuInfo struct {
|
||
AvailableTimes *AvailableTimesInfo `json:"available_times"`
|
||
BoxNum string `json:"box_num"`
|
||
BoxPrice string `json:"box_price"`
|
||
LocationCode string `json:"location_code"`
|
||
Price string `json:"price"`
|
||
SkuID string `json:"sku_id"`
|
||
Spec string `json:"spec"`
|
||
Stock string `json:"stock"`
|
||
Upc string `json:"upc"`
|
||
Weight int `json:"weight"`
|
||
}
|
||
|
||
type AppFood struct {
|
||
AppFoodCode string `json:"app_food_code"`
|
||
AppPoiCode string `json:"app_poi_code"`
|
||
BoxNum float64 `json:"box_num"`
|
||
BoxPrice float64 `json:"box_price"`
|
||
CategoryCode string `json:"category_code"`
|
||
CategoryName string `json:"category_name"`
|
||
Ctime int `json:"ctime"`
|
||
Description string `json:"description"`
|
||
IsSp int `json:"isSp"`
|
||
IsSoldOut int `json:"is_sold_out"`
|
||
IsSpecialty int `json:"is_specialty"`
|
||
MinOrderCount int `json:"min_order_count"`
|
||
Name string `json:"name"`
|
||
Picture string `json:"picture"`
|
||
PictureList []string `json:"pictureList"`
|
||
PictureContents string `json:"picture_contents"`
|
||
Price float64 `json:"price"`
|
||
SecondaryCategoryCode string `json:"secondary_category_code"`
|
||
SecondaryCategoryName string `json:"secondary_category_name"`
|
||
Sequence int `json:"sequence"`
|
||
Skus string `json:"skus"`
|
||
SkuList []*SkuInfo `json:"skuList"`
|
||
TagID int `json:"tag_id"`
|
||
Unit string `json:"unit"`
|
||
Utime int `json:"utime"`
|
||
ZhName string `json:"zh_name"`
|
||
}
|
||
|
||
// 美团分类没有ID,就以名字为唯一标识,不论级别都必须不能重名
|
||
// name(和originName)的长度不能超过10个字符(字符,不是字节)
|
||
// 创建一级分类,originName为空,name为新分类名,secondaryName为空
|
||
// 修改一级分类,originName为分类名,name为分类新名,secondaryName为空
|
||
// 创建二级分类,secondaryName为二级分类名,
|
||
// (如果originName为空,同时创建一级分类,所以如果只是创建二级分类,originName与name要填一样的,此时sequence指的二级分类的sequence,一级分类的sequence为缺省值)
|
||
// 修改二级分类,originName为二级分类名,name为二级分类新名,secondaryName为空
|
||
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)
|
||
if err != nil {
|
||
if err2, ok := err.(*utils.ErrorWithCode); ok {
|
||
if err2.IntCode() == 833 {
|
||
return nil
|
||
}
|
||
}
|
||
}
|
||
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
|
||
}
|
||
|
||
// 商品名最长30个字符(非字节)
|
||
// 不能包含敏感词:特级
|
||
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
|
||
// 返回的app_poi_code始终是空,手动建的商品app_food_code也为空(导致无法通过API删除)
|
||
func (a *API) RetailList(poiCode string, offset, limit int) (foodList []*AppFood, err error) {
|
||
result, err := a.AccessAPI("retail/list", true, map[string]interface{}{
|
||
KeyAppPoiCode: poiCode,
|
||
"offset": offset,
|
||
"limit": limit,
|
||
})
|
||
if err == nil {
|
||
if err = utils.Map2StructByJson(result, &foodList, true); err == nil {
|
||
for _, food := range foodList {
|
||
utils.UnmarshalUseNumber([]byte(food.Skus), &food.SkuList)
|
||
food.PictureList = strings.Split(food.Picture, ",")
|
||
}
|
||
}
|
||
}
|
||
return foodList, err
|
||
}
|
||
|
||
func (a *API) RetailBatchInitData(poiCode string, foodDataList []map[string]interface{}) (err error) {
|
||
_, err = a.AccessAPI("retail/batchinitdata", false, map[string]interface{}{
|
||
KeyAppPoiCode: poiCode,
|
||
"food_data": string(utils.MustMarshal(foodDataList)),
|
||
})
|
||
return err
|
||
}
|
||
|
||
func (a *API) RetailSkuPrice(poiCode string, foodData []*BareStoreFoodInfo) (err error) {
|
||
_, err = a.AccessAPI("retail/sku/price", false, map[string]interface{}{
|
||
KeyAppPoiCode: poiCode,
|
||
"food_data": string(utils.MustMarshal(foodData)),
|
||
})
|
||
return err
|
||
}
|
||
|
||
func (a *API) RetailSkuStock(poiCode string, foodData []*BareStoreFoodInfo) (err error) {
|
||
_, err = a.AccessAPI("retail/sku/stock", false, map[string]interface{}{
|
||
KeyAppPoiCode: poiCode,
|
||
"food_data": string(utils.MustMarshal(foodData)),
|
||
})
|
||
return err
|
||
}
|
||
|
||
func (a *API) RetailGet(poiCode, foodCode string) (food *AppFood, err error) {
|
||
result, err := a.AccessAPI("retail/get", true, map[string]interface{}{
|
||
KeyAppPoiCode: poiCode,
|
||
KeyAppFoodCode: foodCode,
|
||
})
|
||
if err == nil {
|
||
if err = utils.Map2StructByJson(result, &food, true); err == nil && food.Skus != "" {
|
||
utils.UnmarshalUseNumber([]byte(food.Skus), &food.SkuList)
|
||
food.PictureList = strings.Split(food.Picture, ",")
|
||
}
|
||
}
|
||
return food, 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": string(utils.MustMarshal(standardSkus)),
|
||
"unstandard_skus": string(utils.MustMarshal(unstandardSkus)),
|
||
})
|
||
return err
|
||
}
|
||
|
||
func (a *API) RetailSkuSellStatus(poiCode string, foodData []map[string]interface{}, sellStatus int) (err error) {
|
||
_, err = a.AccessAPI("retail/sku/sellStatus", false, map[string]interface{}{
|
||
KeyAppPoiCode: poiCode,
|
||
"food_data": string(utils.MustMarshal(foodData)),
|
||
"sell_status": sellStatus,
|
||
})
|
||
return err
|
||
}
|
||
|
||
func (a *API) RetailSkuSellStatus2(poiCode string, foodData []*BareStoreFoodInfo, sellStatus int) (err error) {
|
||
_, err = a.AccessAPI("retail/sku/sellStatus", false, map[string]interface{}{
|
||
KeyAppPoiCode: poiCode,
|
||
"food_data": string(utils.MustMarshal(foodData)),
|
||
"sell_status": sellStatus,
|
||
})
|
||
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 []*RetailTag, err error) {
|
||
result, err := a.AccessAPI("retail/getSpTagIds", true, nil)
|
||
if err == nil {
|
||
if err = utils.Map2StructByJson(result, &tagIds, false); err == nil {
|
||
return tagIds, 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
|
||
}
|
||
|
||
func IsErrCategoryExist(err error) (isExist bool) {
|
||
return utils.IsErrMatch(err, utils.Int2Str(ErrCodeSkuCategoryExist), nil)
|
||
}
|
||
|
||
func IsErrCategoryNotExist(err error) (isExist bool) {
|
||
return utils.IsErrMatch(err, utils.Int2Str(ErrCodeSkuCategoryNotExist), nil)
|
||
}
|
||
|
||
func IsErrSkuNotExist(err error) (isExist bool) {
|
||
return utils.IsErrMatch(err, utils.Int2Str(ErrCodeNoAppFood), nil)
|
||
}
|