Files
baseapi/platformapi/mtwmapi/retail.go

372 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package mtwmapi
import (
"regexp"
"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这个接口的批量最大值
)
const (
SellStatusOnline = 0 // 上架
SellStatusOffline = 1 // 下架
)
var (
retailBatchFailedSkuReg = regexp.MustCompile(`((?:\d+;)+)`)
)
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"`
}
type AppFoodResult struct {
AppFoodCode string `json:"app_food_code"`
ErrorMsg string `json:"error_msg"`
}
// 美团分类没有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
}
// 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 handleRetailBatchResult(result interface{}) (failedFoodList []*AppFoodResult, err error) {
if msg, ok := result.(string); ok && msg != "" {
err = utils.UnmarshalUseNumber([]byte(msg), &failedFoodList)
}
return failedFoodList, err
}
func handleRetailBatchResultByRegexp(result interface{}) (failedFoodList []*AppFoodResult, err error) {
if msg, ok := result.(string); ok && msg != "" {
findList := retailBatchFailedSkuReg.FindStringSubmatch(msg)
if len(findList) == 2 {
ids := strings.Split(strings.Trim(findList[1], ";"), ";")
if len(ids) > 0 {
failedFoodList = make([]*AppFoodResult, len(ids))
for k, v := range ids {
failedFoodList[k] = &AppFoodResult{
AppFoodCode: v,
ErrorMsg: "",
}
}
}
}
}
return failedFoodList, err
}
// 商品名最长30个字符非字节
func (a *API) RetailInitData(trackInfo, poiCode, foodCode string, params map[string]interface{}) (err error) {
_, err = a.AccessAPI2("retail/initdata", false, utils.MergeMaps(map[string]interface{}{
KeyAppPoiCode: poiCode,
KeyAppFoodCode: foodCode,
}, params), resultKeyData, trackInfo)
return err
}
func (a *API) RetailBatchInitData(trackInfo, poiCode string, foodDataList []map[string]interface{}) (failedFoodList []*AppFoodResult, err error) {
result, err := a.AccessAPI2("retail/batchinitdata", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
"food_data": string(utils.MustMarshal(foodDataList)),
}, resultKeyMsg, trackInfo)
if err == nil {
failedFoodList, err = handleRetailBatchResult(result)
}
return failedFoodList, err
}
func (a *API) RetailSkuPrice(trackInfo, poiCode string, foodData []*BareStoreFoodInfo) (failedFoodList []*AppFoodResult, err error) {
result, err := a.AccessAPI2("retail/sku/price", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
"food_data": string(utils.MustMarshal(foodData)),
}, resultKeyMsg, trackInfo)
if err == nil {
failedFoodList, err = handleRetailBatchResult(result)
}
return failedFoodList, err
}
func (a *API) RetailSkuStock(trackInfo, poiCode string, foodData []*BareStoreFoodInfo) (failedFoodList []*AppFoodResult, err error) {
result, err := a.AccessAPI2("retail/sku/stock", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
"food_data": string(utils.MustMarshal(foodData)),
}, resultKeyMsg, trackInfo)
if err == nil {
failedFoodList, err = handleRetailBatchResult(result)
}
return failedFoodList, err
}
// retail/sku/sellStatus在部分失败时会返回错误其它相应的批处理函数则会返回成功
func (a *API) RetailSkuSellStatus(trackInfo, poiCode string, foodData []*BareStoreFoodInfo, sellStatus int) (failedFoodList []*AppFoodResult, err error) {
_, err = a.AccessAPI2("retail/sku/sellStatus", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
"food_data": string(utils.MustMarshal(foodData)),
"sell_status": sellStatus,
}, resultKeyMsg, trackInfo)
if err != nil {
if errExt, ok := err.(*utils.ErrorWithCode); ok {
failedFoodList, _ = handleRetailBatchResultByRegexp(errExt.ErrMsg())
}
}
return failedFoodList, 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) RetailDelete(trackInfo, poiCode, foodCode string) (err error) {
_, err = a.AccessAPI2("retail/delete", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
KeyAppFoodCode: foodCode,
}, resultKeyData, trackInfo)
return err
}
func (a *API) RetailSkuDelete(trackInfo, poiCode, foodCode, skuID string) (err error) {
_, err = a.AccessAPI2("retail/sku/delete", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
KeyAppFoodCode: foodCode,
"sku_id": skuID,
}, resultKeyData, trackInfo)
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(trackInfo, 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.AccessAPI2("retailCat/batchdelete/catandretail", false, params, resultKeyData, trackInfo)
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) (isNotExist bool) {
return utils.IsErrMatch(err, utils.Int2Str(ErrCodeSkuCategoryNotExist), nil)
}
func IsErrSkuNotExist(err error) (isExist bool) {
return utils.IsErrMatch(err, utils.Int2Str(ErrCodeNoAppFood), nil)
}