Merge branch 'master' into get-store

This commit is contained in:
gazebo
2019-06-24 10:12:27 +08:00
67 changed files with 3112 additions and 729 deletions

View File

@@ -137,7 +137,7 @@ func (a *API) AccessAPI(apiStr string, params map[string]interface{}) (retVal Re
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (errLevel string, err error) {
func(response *http.Response, jsonResult1 map[string]interface{}) (errLevel string, err error) {
status := jsonResult1["status"].(string)
if status == StatusCodeSuccess {
retVal = jsonResult1
@@ -179,11 +179,7 @@ func (a *API) CoordinateConvert(lng, lat float64, coordsys string) (retLng, retL
// 这里的District指的是实际的District有些市是没有区的比如东莞这种情况下返回的区码是一个假的区域即市的编码加上9000000
func (a *API) GetCoordinateDistrictCode(lng, lat float64) (districtCode int) {
params := map[string]interface{}{
"location": fmt.Sprintf("%.6f,%.6f", lng, lat),
}
result, err := a.AccessAPI("geocode/regeo", params)
// baseapi.SugarLogger.Debug(utils.Format4Output(result, false))
result, err := a.GetCoordinateAreaInfo(lng, lat)
if err == nil {
addressComponent := result["regeocode"].(map[string]interface{})["addressComponent"].(map[string]interface{})
if strAdcode, ok := addressComponent["adcode"].(string); ok {
@@ -196,6 +192,25 @@ func (a *API) GetCoordinateDistrictCode(lng, lat float64) (districtCode int) {
return districtCode
}
func (a *API) GetCoordinateAreaInfo(lng, lat float64) (areaInfo map[string]interface{}, err error) {
params := map[string]interface{}{
"location": fmt.Sprintf("%.6f,%.6f", lng, lat),
}
result, err := a.AccessAPI("geocode/regeo", params)
return result, err
}
func (a *API) GetCoordinateTownInfo(lng, lat float64) (townName, townCode string) {
result, err := a.GetCoordinateAreaInfo(lng, lat)
// baseapi.SugarLogger.Debug(utils.Format4Output(result, false))
if err == nil {
addressComponent := result["regeocode"].(map[string]interface{})["addressComponent"].(map[string]interface{})
townName = utils.Interface2String(addressComponent["township"])
townCode = utils.Interface2String(addressComponent["towncode"])
}
return townName, townCode
}
// 这里的District指的是地点不是实际上的区具体级别看level
// 这个函数返回的可能不是同级别的地点
func (a *API) GetDistricts(subDistrict int, keywords string) (districtList []*District, err error) {

View File

@@ -4,6 +4,7 @@ import (
"testing"
"git.rosy.net.cn/baseapi"
"git.rosy.net.cn/baseapi/utils"
"go.uber.org/zap"
)
@@ -63,13 +64,22 @@ func TestGetCoordinateDistrictCode(t *testing.T) {
}
}
func TestGetCoordinateTownInfo(t *testing.T) {
dongguanLng := 120.74567
dongguanLat := 31.69742
townName, townCode := autonaviAPI.GetCoordinateTownInfo(dongguanLng, dongguanLat)
if townName != "周市镇" || townCode != "320583102000" {
t.Fatalf("townName:%s, townCode:%s", townName, townCode)
}
}
func TestGetDistricts(t *testing.T) {
districtList, err := autonaviAPI.GetDistricts(1, "东莞")
districtList, err := autonaviAPI.GetDistricts(4, "常熟市")
if err != nil {
t.Fatal(err)
}
if len(districtList) == 0 {
t.Fatal("should have ditrict")
}
// t.Log(utils.Format4Output(districtList, false))
t.Log(utils.Format4Output(districtList, false))
}

View File

@@ -118,7 +118,7 @@ func (a *API) AccessAPI(action string, params interface{}) (retVal *ResponseResu
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (result string, err error) {
func(response *http.Response, jsonResult1 map[string]interface{}) (result string, err error) {
code := int(utils.MustInterface2Int64(jsonResult1["code"]))
retVal = &ResponseResult{
Code: code,

View File

@@ -112,8 +112,8 @@ func (a *API) Decrypt(msg string) (decryptedMsg string, err error) {
func (a *API) RegisterCallback(callbackTagList []string, token, aesKey, urlStr string) (err error) {
a.locker.Lock()
oldCallbackToken := a.callbackToken
oldCallbackAESKey := a.callbackAESKey
// oldCallbackToken := a.callbackToken
// oldCallbackAESKey := a.callbackAESKey
a.callbackToken = token
a.callbackAESKey, _ = base64.StdEncoding.DecodeString(aesKey + "=")
a.locker.Unlock()
@@ -126,12 +126,12 @@ func (a *API) RegisterCallback(callbackTagList []string, token, aesKey, urlStr s
"url": urlStr,
})
}
if err != nil {
a.locker.Lock()
defer a.locker.Unlock()
a.callbackToken = oldCallbackToken
a.callbackAESKey = oldCallbackAESKey
}
// if err != nil {
// a.locker.Lock()
// defer a.locker.Unlock()
// a.callbackToken = oldCallbackToken
// a.callbackAESKey = oldCallbackAESKey
// }
return err
}

View File

@@ -128,7 +128,7 @@ func (a *API) AccessAPI(action string, params map[string]interface{}, bodyMap ma
params2["access_token"] = accessToken
} else {
params2["accessKey"] = a.GetAppID()
timestamp := time.Now().Unix()
timestamp := time.Now().UnixNano() / 1000000
params2["timestamp"] = timestamp
params2["signature"] = a.signParams(timestamp)
}
@@ -147,7 +147,7 @@ func (a *API) AccessAPI(action string, params map[string]interface{}, bodyMap ma
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (result string, err error) {
func(response *http.Response, jsonResult1 map[string]interface{}) (result string, err error) {
errCode := int(utils.MustInterface2Int64(jsonResult1["errcode"]))
if errCode == ResponseCodeSuccess {
retVal = jsonResult1

View File

@@ -1,7 +1,6 @@
package ebaiapi
import (
"fmt"
"strings"
"git.rosy.net.cn/baseapi/utils"
@@ -11,15 +10,17 @@ const (
ActivityTypeDirectDown = 2 // 商品直降
ActivityTypeFullDiscount = 8 // 品类满减
ActivityPFBaidu = 1
ActivityPFELM = 2
ActivityPFAll = 4
ActivityPFBaidu = 1 // 星选
ActivityPFELM = 2 // 饿了么
ActivityPFAll = 4 // 星选&饿了么
ActivityConflictShare = 0
ActivityConfilictExclude = 1
ActivityConflictShare = 0 // 与全店满减等其他活动同享
ActivityConfilictExclude = 1 // 与全店满减等其他活动互斥
ActivityShowStatusEnabled = 2
ActivityShowStatusDisabled = 4
MaxActivitySkuBatchSize = 100 // 使用商品id的活动商品与商品自定义id互斥。一批最多100个
// 下面两个是做什么用的?
// ActivityShowStatusEnabled = 2
// ActivityShowStatusDisabled = 4
)
type ActivityRule struct {
@@ -45,40 +46,50 @@ type ActivityInfo struct {
PromotionSkuDesc string `json:"promotion_sku_desc,omitempty"` // 店铺页活动商品的分类名称不超过8个字。
Rule []*ActivityRule `json:"rule,omitempty"`
BaiduShopID int64 `json:"baiduShopID,omitempty"`
ShowStatus int `json:"showStatus,omitempty"`
BaiduShopID int64 `json:"baidu_shop_id,omitempty"` // 这个用于查询时返回值,设置时这里设置的值会被忽略
// ShowStatus int `json:"showStatus,omitempty"`
}
type ActivitySkuInfo struct {
type ActivitySkuInfo4Add struct {
SkuID string `json:"sku_id"`
Stock int `json:"stock"`
PromotionPrice float32 `json:"special_price"` // 直降用,复用
StoreUserLimit int `json:"store_user_limit"` // 直降用
StoreUserLimit int `json:"store_user_limit"`
SpecialPrice int64 `json:"special_price"`
}
type ActivitySkuInfoEx struct {
ActivitySkuInfo
BaiduShopID int64 `json:"baidu_shop_id"`
UpcName string `json:"upc_name"`
OriginalPrice float32 `json:"original_price"`
type ActivitySkuInfo4List struct {
BaiduShopID int64 `json:"baidu_shop_id"`
SkuID string `json:"sku_id"`
UpcName string `json:"upc_name"`
Stock int `json:"stock"`
PromotionPrice float32 `json:"promotion_price"` // 商品活动价,单位元,品类满减活动不返回活动价。
OriginalPrice float32 `json:"original_price"` // 添加商品时商品原价,单位元。
}
type ActivitySkuInfo4Update struct {
ActivitySkuInfo
BaiduShopID int64 `json:"baidu_shop_id"`
ShopID string `json:"shop_id"`
CustomSkuID string `json:"custom_sku_id"`
BaiduShopID int64 `json:"baidu_shop_id,omitempty"`
ShopID string `json:"shop_id,omitempty"`
SkuID string `json:"sku_id,omitempty"`
CustomSkuID string `json:"custom_sku_id,omitempty"`
Stock int `json:"stock,omitempty"`
StoreUserLimit int `json:"store_user_limit,omitempty"`
SpecialPrice int64 `json:"special_price,omitempty"`
}
type ActivitySkuListInfo struct {
ActivityID int64 `json:"activity_id"`
ActivityType int `json:"activity_type"`
ActivityName string `json:"activity_name"`
SkuList []*ActivitySkuInfoEx `json:"sku_list"`
ActivityID int64 `json:"activity_id"`
ActivityType int `json:"activity_type"`
ActivityName string `json:"activity_name"`
SkuList []*ActivitySkuInfo4List `json:"sku_list"`
}
// 创建商品营销活动
// https://open-be.ele.me/dev/api/doc/v3/#api-Marketing-activity_create
func (a *API) ActivityCreate(shopID string, baiduShopID, supplierID int64, activity *ActivityInfo) (activityID int64, err error) {
activity.BaiduShopID = 0
params := utils.Struct2FlatMap(activity)
result, err := a.AccessAPI("activity.create", utils.MergeMaps(params, a.genShopIDParams(shopID, baiduShopID, supplierID)))
if err == nil {
@@ -87,7 +98,10 @@ func (a *API) ActivityCreate(shopID string, baiduShopID, supplierID int64, activ
return 0, err
}
// 更新活动信息
// https://open-be.ele.me/dev/api/doc/v3/#api-Marketing-activity_update
func (a *API) ActivityUpdate(activityID int64, shopID string, baiduShopID, supplierID int64, activity *ActivityInfo) (newActivityID int64, err error) {
activity.BaiduShopID = 0
params := utils.Struct2FlatMap(activity)
params[KeyActivityID] = activityID
result, err := a.AccessAPI("activity.update", utils.MergeMaps(params, a.genShopIDParams(shopID, baiduShopID, supplierID)))
@@ -97,6 +111,8 @@ func (a *API) ActivityUpdate(activityID int64, shopID string, baiduShopID, suppl
return 0, err
}
// 下线商品营销活动
// https://open-be.ele.me/dev/api/doc/v3/#api-Marketing-activity_disable
func (a *API) ActivityDisable(activityID int64, shopID string, baiduShopID, supplierID int64) (err error) {
params := a.genShopIDParams(shopID, baiduShopID, supplierID)
params[KeyActivityID] = activityID
@@ -104,6 +120,8 @@ func (a *API) ActivityDisable(activityID int64, shopID string, baiduShopID, supp
return err
}
// 查看活动信息
// https://open-be.ele.me/dev/api/doc/v3/#api-Marketing-activity_get
func (a *API) ActivityGet(activityID int64, shopID string, baiduShopID, supplierID int64) (activityInfo *ActivityInfo, err error) {
params := a.genShopIDParams(shopID, baiduShopID, supplierID)
params[KeyActivityID] = activityID
@@ -115,7 +133,9 @@ func (a *API) ActivityGet(activityID int64, shopID string, baiduShopID, supplier
return activityInfo, err
}
func (a *API) ActivitySkuAddBatch(activityID int64, shopID string, baiduShopID int64, activityType int, skuList []*ActivitySkuInfo, isSkuIDCustom bool) (successIDs []string, err error) {
// 批量添加活动商品
// https://open-be.ele.me/dev/api/doc/v3/#api-Marketing-activity_sku_add_batch
func (a *API) ActivitySkuAddBatch(activityID int64, shopID string, baiduShopID int64, activityType int, skuList []*ActivitySkuInfo4Add, isSkuIDCustom bool) (successIDs []string, err error) {
params := a.genShopIDParams(shopID, baiduShopID, 0)
params[KeyActivityID] = activityID
skusKey := "activity_skus"
@@ -130,7 +150,7 @@ func (a *API) ActivitySkuAddBatch(activityID int64, shopID string, baiduShopID i
return nil, err
}
func skuList2Str(activityType int, skuList []*ActivitySkuInfo, isSkuIDCustom bool) string {
func skuList2Str(activityType int, skuList []*ActivitySkuInfo4Add, isSkuIDCustom bool) string {
skuList2 := []string{}
for _, v := range skuList {
strList := []string{
@@ -138,14 +158,18 @@ func skuList2Str(activityType int, skuList []*ActivitySkuInfo, isSkuIDCustom boo
utils.Int2Str(v.Stock),
}
if activityType == ActivityTypeDirectDown {
strList = append(strList, fmt.Sprintf("%.2f", v.PromotionPrice))
strList = append(strList, utils.Int2Str(v.StoreUserLimit))
strList = append(strList, utils.Int64ToStr(v.SpecialPrice))
if v.StoreUserLimit > 0 {
strList = append(strList, utils.Int2Str(v.StoreUserLimit))
}
}
skuList2 = append(skuList2, strings.Join(strList, ":"))
}
return strings.Join(skuList2, ";")
}
// 批量删除活动商品
// https://open-be.ele.me/dev/api/doc/v3/#api-Marketing-activity_sku_delete_batch
func (a *API) ActivitySkuDeleteBatch(activityID int64, shopID string, baiduShopID int64, skuIDs []string, isSkuIDCustom bool) (successIDs []string, err error) {
params := a.genShopIDParams(shopID, baiduShopID, 0)
params[KeyActivityID] = activityID
@@ -161,6 +185,8 @@ func (a *API) ActivitySkuDeleteBatch(activityID int64, shopID string, baiduShopI
return nil, err
}
// 批量添加活动商品
// https://open-be.ele.me/dev/api/doc/v3/#api-Marketing-activity_sku_add_batch
func (a *API) ActivitySkuList(activityID int64, shopID string, baiduShopID, supplierID int64, skuIDs []string, isSkuIDCustom bool) (activityInfo *ActivitySkuListInfo, err error) {
params := a.genShopIDParams(shopID, baiduShopID, supplierID)
params[KeyActivityID] = activityID
@@ -179,35 +205,17 @@ func (a *API) ActivitySkuList(activityID int64, shopID string, baiduShopID, supp
params["page"] = page
result, err = a.AccessAPI("activity.sku.list", params)
if err == nil {
// tmpActivityInfo := &ActivitySkuListInfo{}
// if err = utils.Map2StructByJson(result.Data, tmpActivityInfo, true); err != nil {
// return nil, err
// }
// if activityInfo == nil {
// activityInfo = tmpActivityInfo
// } else {
// activityInfo.SkuList = append(activityInfo.SkuList, tmpActivityInfo.SkuList...)
// }
resultMap := result.Data.(map[string]interface{})
if activityInfo == nil {
activityInfo = &ActivitySkuListInfo{
ActivityID: activityID,
ActivityType: int(utils.Str2Int64(utils.Interface2String(resultMap["activity_type"]))),
ActivityName: utils.Interface2String(resultMap["activity_name"]),
if err = utils.Map2StructByJson(resultMap, &activityInfo, true); err != nil {
return nil, err
}
}
for _, v := range resultMap["sku_list"].([]interface{}) {
skuMap := v.(map[string]interface{})
activityInfo.SkuList = append(activityInfo.SkuList, &ActivitySkuInfoEx{
ActivitySkuInfo: ActivitySkuInfo{
SkuID: utils.Interface2String(skuMap["sku_id"]),
Stock: int(utils.Str2Int64(utils.Interface2String(skuMap["stock"]))),
PromotionPrice: float32(utils.Interface2Float64WithDefault(skuMap["promotion_price"], 0)),
},
BaiduShopID: utils.Str2Int64(utils.Interface2String(skuMap["baidu_shop_id"])),
OriginalPrice: float32(utils.Interface2Float64WithDefault(skuMap["original_price"], 0)),
UpcName: utils.Interface2String(skuMap["upc_name"]),
})
} else {
var skuList []*ActivitySkuInfo4List
if err = utils.Map2StructByJson(resultMap["sku_list"], &skuList, true); err != nil {
return nil, err
}
activityInfo.SkuList = append(activityInfo.SkuList, skuList...)
}
if len(activityInfo.SkuList) >= int(utils.MustInterface2Int64(resultMap["total"])) {
break
@@ -220,6 +228,8 @@ func (a *API) ActivitySkuList(activityID int64, shopID string, baiduShopID, supp
return activityInfo, err
}
// 批量更新活动商品
// https://open-be.ele.me/dev/api/doc/v3/#api-Marketing-activity_sku_update_batch
func (a *API) ActivitySkuUpdateBatch(activityID int64, actSkuInfoList []*ActivitySkuInfo4Update) (faildInfoList []string, err error) {
result, err := a.AccessAPI("activity.sku.update.batch", map[string]interface{}{
KeyActivityID: activityID,

View File

@@ -7,8 +7,10 @@ import (
"git.rosy.net.cn/baseapi/utils"
)
const testActivityID = 3000000000517188
func TestActivityCreate(t *testing.T) {
activityInfo, err := api.ActivityCreate("", 2235877569, 0, &ActivityInfo{
activityInfo, err := api.ActivityCreate("", testShopBaiduID, 0, &ActivityInfo{
ActivityName: "测试活动0403",
StartTime: time.Now().Unix(),
EndTime: time.Now().Add(24 * time.Hour).Unix(),
@@ -29,8 +31,24 @@ func TestActivityCreate(t *testing.T) {
}
}
func TestActivitySkuAddBatch(t *testing.T) {
result, err := api.ActivitySkuAddBatch(testActivityID, "", testShopBaiduID, ActivityTypeDirectDown, []*ActivitySkuInfo4Add{
&ActivitySkuInfo4Add{
SkuID: "3594",
Stock: 9,
SpecialPrice: 100,
StoreUserLimit: 9,
},
}, true)
if err != nil {
t.Fatal(err)
} else {
t.Log(utils.Format4Output(result, false))
}
}
func TestActivityUpdate(t *testing.T) {
activityInfo, err := api.ActivityUpdate(6000268610, "", 2235877569, 0, &ActivityInfo{
activityInfo, err := api.ActivityUpdate(6000268610, "", testShopBaiduID, 0, &ActivityInfo{
ActivityName: "测试活动0403",
StartTime: time.Now().Unix(),
EndTime: time.Now().Add(24 * time.Hour).Unix(),
@@ -52,7 +70,7 @@ func TestActivityUpdate(t *testing.T) {
}
func TestActivityGet(t *testing.T) {
activityInfo, err := api.ActivityGet(6000268610, "", 2235877569, 0)
activityInfo, err := api.ActivityGet(3000000000517188, "", testShopBaiduID, 0)
if err != nil {
t.Fatal(err)
} else {
@@ -61,14 +79,14 @@ func TestActivityGet(t *testing.T) {
}
func TestActivityDisable(t *testing.T) {
err := api.ActivityDisable(6000268604, "", 2235877569, 0)
err := api.ActivityDisable(6000268604, "", testShopBaiduID, 0)
if err != nil {
t.Fatal(err)
}
}
func TestActivitySkuList(t *testing.T) {
activityInfo, err := api.ActivitySkuList(6000058442, "", 0, 2233065879, nil, false)
activityInfo, err := api.ActivitySkuList(3000000000517188, "", testShopBaiduID, 0, nil, false)
if err != nil {
t.Fatal(err)
} else {

View File

@@ -37,6 +37,38 @@ type CallbackMsg struct {
Cmd string `json:"cmd"`
Timestamp int64 `json:"timestamp"`
Body map[string]interface{} `json:"body"`
Data interface{} `json:"data"`
}
type CBPartRefundInfo struct {
OrderID int64 `json:"order_id"`
RefundID string `json:"refund_id"`
Type int `json:"type"`
Status int `json:"status"`
AdditionReason string `json:"addition_reason"`
Photos []string `json:"photos"`
ReasonType string `json:"reason_type"`
Reason string `json:"reason"`
RefundProducts []*struct {
SkuID string `json:"sku_id"`
Upc string `json:"upc"`
CustomSkuID string `json:"custom_sku_id"`
Name string `json:"name"`
Number int `json:"number"`
TotalRefund int64 `json:"total_refund"`
ShopEleRefund int64 `json:"shop_ele_refund"`
} `json:"refund_products"`
RefundPrice int64 `json:"refund_price"`
}
type CBUserCancelInfo struct {
OrderID int64 `json:"order_id"`
CancelReason string `json:"cancel_reason"`
AdditionReason string `json:"addition_reason"`
RefuseReason string `json:"refuse_reason"`
Pictures []string `json:"pictures"`
Type int `json:"type"`
CancelType int `json:"cancel_type"`
}
func (a *API) Err2CallbackResponse(cmd string, err error, data interface{}) *CallbackResponse {
@@ -107,6 +139,20 @@ func (a *API) GetCallbackMsg(request *http.Request) (msg *CallbackMsg, callbackR
}
msg.Cmd = GetCmd(request)
msg.Timestamp = utils.Str2Int64(utils.Interface2String(request.FormValue("timestamp")))
var tmpObj interface{}
switch msg.Cmd {
case CmdOrderPartRefund:
var partRefundData CBPartRefundInfo
tmpObj = &partRefundData
case CmdOrderUserCancel:
var userCancelData CBUserCancelInfo
tmpObj = &userCancelData
}
if tmpObj != nil {
if utils.Map2StructByJson(msg.Body, tmpObj, true) == nil {
msg.Data = tmpObj
}
}
return msg, nil
}
return nil, a.Err2CallbackResponse("", err, nil)

View File

@@ -112,7 +112,7 @@ func (a *API) AccessAPI(cmd string, body map[string]interface{}) (retVal *Respon
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (result string, err error) {
func(response *http.Response, jsonResult1 map[string]interface{}) (result string, err error) {
Body := jsonResult1["body"].(map[string]interface{})
retVal = &ResponseResult{
ErrNo: int(utils.MustInterface2Int64(Body["errno"])),
@@ -125,7 +125,7 @@ func (a *API) AccessAPI(cmd string, body map[string]interface{}) (retVal *Respon
baseapi.SugarLogger.Debugf("ebai AccessAPI failed, jsonResult1:%s", utils.Format4Output(jsonResult1, true))
newErr := utils.NewErrorIntCode(retVal.Error, retVal.ErrNo)
// todo 临时处理超过阈值错...
if newErr.IntCode() == 20501 && strings.Index(retVal.Error, "阈值") >= 0 {
if (newErr.IntCode() == 20501 && strings.Index(retVal.Error, "阈值") >= 0) || (newErr.IntCode() == 20502 && strings.Index(retVal.Error, "系统繁忙") >= 0) {
return platformapi.ErrLevelExceedLimit, newErr
}
return platformapi.ErrLevelCodeIsNotOK, newErr

View File

@@ -12,8 +12,8 @@ import (
)
const (
testShopBaiduID = 2233976901
testShopID = "100077"
testShopBaiduID = 2267254343
testShopID = "2"
)
var (
@@ -25,14 +25,13 @@ func init() {
logger, _ := zap.NewDevelopment()
sugarLogger = logger.Sugar()
baseapi.Init(sugarLogger)
// sandbox
api = New("62923", "aa4cdc6c1108486b")
// sandbox果园测试门店
api = New("62289", "d3ec2358d6a819ea")
// prod
// api = New("34665", "c3db75b754ea2d89")
api.SetStoreCookie("WMUSS", "AADIBAABbDlEpGl47c1EyBFcJSidBTBJHFHZEXyMSdBllJTZ9AUNOKV0tZFB9FlRVM73gEAIHRjBVagwAAHh98X2oPJ34Gal0ofFJBYXZ2Xnc6LCEXWQVnVxs7LDlaKBlFNz9DPCogYyZxJQhoHGVfVRIBa2oFUkEfDm1YZxZwLEwvZMjpB18rjw%7E3CaMQAo")
api.SetStoreCookie("WMSTOKEN", "HwXAAB9SGxnTT8pbEwWRDQsNGB3Y09_PF5rO157QUcoLRQAAAoKlgUEiVTOhIf5LjkCoFpAwwCaAAAUr-GEu-yDBeNAQAAHNneF25-uRjhYtgX4rsAAGHurh8C5GAQAA")
api.SetStoreCookie("WMUSS", "4AAPQCAAB5PF0aUGcBVzoRTCEkOFhFIhx-Yk9vN2EfPHYoLlROKBEsQmAUQjhNUgRt0ADAP5x-RFklwAAdjxGO11iOj8xKXYSSDIJb2BcPghsaklNfQwGS10JOVRFfhAiYElhEXFXIzoJKyloCGdwdFE6Qk9FRxojUFN3FVEHNjJPZJu4Bt9nxQ13cwoMbjA")
api.SetStoreCookie("WMSTOKEN", "AcAANQZAABbC04rUBZFc2UYanlocDAaP0dcfzZCeS1SHQ1qJ15ExgAA13A2dGLjdbcitBZJu4Bn6B_g6cZAAA0tyyFm8cdBaNAQAAwug8HTG0xRjwt1UZzbcAAN7ofRO")
}
func TestTest(t *testing.T) {

View File

@@ -0,0 +1,5 @@
package ebaiapi
const (
ErrOrderIsNotPartRefund = 301006 // 该订单非部分退款订单
)

View File

@@ -30,42 +30,42 @@ const (
)
const (
WaybillStatusNew = "2"
WaybillStatusRequestDelivery = "3"
WaybillStatusWait4Courier = "4"
WaybillStatusCourierAccepted = "7"
WaybillStatusCourierPickedup = "8"
WaybillStatusDeliveryCancled = "15"
WaybillStatusFinished = "16"
WaybillStatusExceptional = "17"
WaybillStatusSelfDelivery = "18"
WaybillStatusNotInDelivering = "19"
WaybillStatusDeliveryRejected = "20"
WaybillStatusNew = "2" // 生成运单
WaybillStatusRequestDelivery = "3" // 请求配送
WaybillStatusWait4Courier = "4" // 等待分配骑士
WaybillStatusCourierAccepted = "7" // 骑士接单
WaybillStatusCourierPickedup = "8" // 骑士取餐
WaybillStatusDeliveryCancled = "15" // 配送取消
WaybillStatusFinished = "16" // 配送完成
WaybillStatusExceptional = "17" // 配送异常
WaybillStatusSelfDelivery = "18" // 自行配送
WaybillStatusDontDeliver = "19" // 不再配送
WaybillStatusDeliveryRejected = "20" // 配送拒单
)
const (
// 订单下行 - order.partrefund.push-部分退款订单信息推送
OrderPartRefuncTypeMerchant = "1" // 表示商户发起的部分退款
OrderPartRefuncTypeCustomer = "2" // 表示用户发起的部分退款
OrderPartRefuncTypeCS = "3" // 表示客服直接发起的部分退款
OrderPartRefuncTypeMerchant = 1 // 表示商户发起的部分退款
OrderPartRefuncTypeCustomer = 2 // 表示用户发起的部分退款
OrderPartRefuncTypeCS = 3 // 表示客服直接发起的部分退款
OrderPartRefundApply = "10" // 表示商家/用户发起部分退款申请
OrderPartRefundSuccess = "20" // 表示部分退款成功
OrderPartRefundUserApplyArbitration = "30" // 用户申请仲裁,客服介入
OrderPartRefundFailed = "40" // 表示部分退款失败
OrderPartRefundMerchantRefused = "50" // 表示商家拒绝用户发起的部分退款申请
OrderPartRefundApply = 10 // 表示商家/用户发起部分退款申请
OrderPartRefundSuccess = 20 // 表示部分退款成功
OrderPartRefundUserApplyArbitration = 30 // 用户申请仲裁,客服介入
OrderPartRefundFailed = 40 // 表示部分退款失败
OrderPartRefundMerchantRefused = 50 // 表示商家拒绝用户发起的部分退款申请
// 订单下行 - order.user.cancel-用户申请订单取消/退款
OrderUserCancelApply = "10" // 发起申请
OrderUserCancelCSIntervene = "20" // 客服介入
OrderUserCancelCSRefused = "30" // 客服拒绝
OrderUserCancelCSAgreed = "40" // 客服同意
OrderUserCancelMerchantRefused = "50" // 商户拒绝
OrderUserCancelMerchantAgreed = "60" // 商家同意
OrderUserCancelInvalid = "70" // 申请失效
OrderUserCancelApply = 10 // 发起申请
OrderUserCancelCSIntervene = 20 // 客服介入
OrderUserCancelCSRefused = 30 // 客服拒绝
OrderUserCancelCSAgreed = 40 // 客服同意
OrderUserCancelMerchantRefused = 50 // 商户拒绝
OrderUserCancelMerchantAgreed = 60 // 商家同意
OrderUserCancelInvalid = 70 // 申请失效
OrderUserCancelTypeBeforeSale = "1" // 表示订单完成前用户全单取消申请流程
OrderUserCancelTypeAfterSale = "2" // 表示订单完成后用户全单退款申请流程
OrderUserCancelTypeBeforeSale = 1 // 表示订单完成前用户全单取消申请流程
OrderUserCancelTypeAfterSale = 2 // 表示订单完成后用户全单退款申请流程
SendImmediatelySelf = 6 // 饿百商家自送的配送状态
)
@@ -73,12 +73,19 @@ const (
const (
OrderFromBaidu = "1"
OrderFromElm = "2"
OrderSkuDiscountTypeZhe = "g_zhe"
OrderSkuDiscountTypeReduce = "g_reduce"
)
const (
UserApplyCancelWaitMinute = 15 // 用户申请取消订单后待处理的最大等待时间(分钟),超时自动同意
)
const (
ListOrderPageSize = 100 // order.list的每页条数
)
type ExpressInfo struct {
OrderID string `json:"order_id"`
ExpressID string `json:"express_id"`
@@ -97,6 +104,205 @@ type RefundSku struct {
Number string `json:"number"`
}
type ProductSubsidyInfo struct {
AgentRate int `json:"agent_rate"`
BaiduRate int `json:"baidu_rate"`
Discount int `json:"discount"`
DiscountDetail []*struct {
ActivityID string `json:"activity_id"`
BaiduRate int `json:"baidu_rate"`
ShopRate int `json:"shop_rate"`
Type string `json:"type"`
} `json:"discount_detail"`
LogisticsRate int `json:"logistics_rate"`
ShopRate int `json:"shop_rate"`
UserRate int `json:"user_rate"`
}
type OrderProductInfo struct {
BaiduProductID string `json:"baidu_product_id"`
SkuID string `json:"sku_id"` // 部分退款信息中是这个而不是BaiduProductID
CustomSkuID string `json:"custom_sku_id"`
PackageAmount int `json:"package_amount"`
PackageFee int `json:"package_fee"`
PackagePrice int `json:"package_price"`
PrescriptionID string `json:"prescription_id"`
ProductAmount int `json:"product_amount"`
Number int `json:"number"` // 部分退款信息中是这个而不是ProductAmount
ProductAttr []interface{} `json:"product_attr"`
ProductCustomIndex string `json:"product_custom_index"`
ProductFeatures []interface{} `json:"product_features"`
ProductFee int `json:"product_fee"`
ProductName string `json:"product_name"`
ProductPrice int `json:"product_price"`
ProductSubsidy *ProductSubsidyInfo `json:"product_subsidy"`
ProductType int `json:"product_type"`
SupplyType int `json:"supply_type"`
TotalFee int `json:"total_fee"`
TotalWeight int `json:"total_weight"`
Upc string `json:"upc"`
WeightType int `json:"weight_type"`
}
type OrderDetailInfo struct {
Discount []*struct {
ActivityID string `json:"activity_id"`
AgentRate int `json:"agent_rate"`
BaiduRate int `json:"baidu_rate"`
Desc string `json:"desc"`
Fee int `json:"fee"`
LogisticsRate int `json:"logistics_rate"`
Products []*struct {
ActivityID string `json:"activity_id"`
ActivityProductNum int `json:"activity_product_num"`
BaiduProductID int64 `json:"baidu_product_id"`
NowPrice int `json:"now_price"`
OrigPrice int `json:"orig_price"`
SavePrice int `json:"save_price"`
} `json:"products,omitempty"`
RuleID string `json:"rule_id"`
ShopRate int `json:"shop_rate"`
Type string `json:"type"`
UserRate int `json:"user_rate,omitempty"`
} `json:"discount"`
Order *struct {
AtshopTime int `json:"atshop_time"`
CancelTime string `json:"cancel_time"`
ColdBoxFee string `json:"cold_box_fee"`
Commission int `json:"commission"`
ConfirmTime string `json:"confirm_time"`
CreateTime string `json:"create_time"`
DeliveryParty int `json:"delivery_party"`
DeliveryPhone string `json:"delivery_phone"`
DeliveryTime int `json:"delivery_time"`
DiscountFee int `json:"discount_fee"`
DownFlag int `json:"down_flag"`
ElemeOrderID string `json:"eleme_order_id"`
ExpectTimeMode int `json:"expect_time_mode"`
Ext struct {
TaoxiFlag int `json:"taoxi_flag"`
} `json:"ext"`
FinishedTime string `json:"finished_time"`
InvoiceTitle string `json:"invoice_title"`
IsColdBoxOrder int `json:"is_cold_box_order"`
IsPrivate int `json:"is_private"`
LatestSendTime int `json:"latest_send_time"`
MealNum string `json:"meal_num"`
NeedInvoice int `json:"need_invoice"`
OrderFlag int `json:"order_flag"`
OrderFrom string `json:"order_from"`
OrderID string `json:"order_id"`
OrderIndex string `json:"order_index"`
PackageFee int `json:"package_fee"`
PayStatus int `json:"pay_status"`
PayType int `json:"pay_type"`
PickupTime int `json:"pickup_time"`
Remark string `json:"remark"`
ResponsibleParty string `json:"responsible_party"`
SendFee int `json:"send_fee"`
SendImmediately int `json:"send_immediately"`
SendTime int `json:"send_time"`
ShopFee int `json:"shop_fee"`
Status int `json:"status"`
TaxerID string `json:"taxer_id"`
TotalFee int `json:"total_fee"`
UserFee int `json:"user_fee"`
} `json:"order"`
Products [][]*OrderProductInfo `json:"products"`
Shop *struct {
BaiduShopID string `json:"baidu_shop_id"`
ID string `json:"id"`
Name string `json:"name"`
} `json:"shop"`
Source string `json:"source"`
User *struct {
Address string `json:"address"`
City string `json:"city"`
Coord *struct {
Latitude string `json:"latitude"`
Longitude string `json:"longitude"`
} `json:"coord"`
CoordAmap *struct {
Latitude string `json:"latitude"`
Longitude string `json:"longitude"`
} `json:"coord_amap"`
District string `json:"district"`
Gender string `json:"gender"`
Name string `json:"name"`
Phone string `json:"phone"`
PrivacyPhone string `json:"privacy_phone"`
Province string `json:"province"`
UserID string `json:"user_id"`
} `json:"user"`
}
type RefundOrderDetailInfo struct {
CustomSkuID string `json:"custom_sku_id"`
Name string `json:"name"`
Number int `json:"number"`
ProductFeatures []interface{} `json:"product_features"`
ProductFee int `json:"product_fee"`
ProductPrice int `json:"product_price"`
ProductSubsidy *ProductSubsidyInfo `json:"product_subsidy"`
SkuID string `json:"sku_id"`
TotalWeight int `json:"total_weight"`
Upc string `json:"upc"`
}
type RefundDetailInfo struct {
ApplyTime string `json:"apply_time"`
CustomSkuID string `json:"custom_sku_id"`
Desc string `json:"desc"`
Name string `json:"name"`
Number int `json:"number"`
RefundID string `json:"refund_id"`
ShopEleRefund int `json:"shop_ele_refund"`
SkuID string `json:"sku_id"`
Status int `json:"status"`
TotalRefund int `json:"total_refund"`
Type int `json:"type"`
Upc string `json:"upc"`
}
type PartRefundInfo struct {
Commission int `json:"commission"`
Fee int `json:"fee"`
OrderDetail []*RefundOrderDetailInfo `json:"order_detail"`
OrderID string `json:"order_id"`
PackageFee int `json:"package_fee"`
RefundDetail []*RefundDetailInfo `json:"refund_detail"`
RefundPrice int `json:"refund_price"`
SendFee int `json:"send_fee"`
ShopFee int `json:"shop_fee"`
TotalPrice int `json:"total_price"`
Type int `json:"type"`
UserFee int `json:"user_fee"`
}
type ListOrderItemInfo struct {
BaiduShopID int64 `json:"baidu_shop_id"`
CreateTime int64 `json:"create_time"`
OrderFrom int `json:"order_from"`
OrderID string `json:"order_id"`
OrderStatus int `json:"order_status"`
PayStatus int `json:"pay_status"`
PayType int `json:"pay_type"`
ShopID string `json:"shop_id"`
Status int `json:"status"`
UserPhone string `json:"user_phone"`
}
type ListOrderInfo struct {
Total int `json:"total"`
Page int `json:"Page"`
Pages int `json:"pages"`
List []*ListOrderItemInfo `json:"list"`
}
// 提供给合作方确认订单所用。 注1、10分钟内未确认的订单系统自动取消。2、确认失败的订单请不要做餐。 2016年7月4号起将由百度外卖负责完成订单。届时对接方无需调用完成订单接口继续调用可能导致订单结算有问题。
func (a *API) OrderConfirm(orderID string) (err error) {
_, err = a.AccessAPI("order.confirm", map[string]interface{}{
@@ -169,6 +375,16 @@ func (a *API) OrderGet(orderID string) (orderMap map[string]interface{}, err err
return nil, err
}
func (a *API) OrderGet2(orderID string) (order *OrderDetailInfo, err error) {
result, err := a.AccessAPI("order.get", map[string]interface{}{
"order_id": orderID,
})
if err == nil {
err = utils.Map2StructByJson(result.Data, &order, true)
}
return order, err
}
// 设置订单快递单号
func (a *API) OrderExpressCreate(shopID string, expressList []*ExpressInfo) (status int, err error) {
result, err := a.AccessAPI("order.express.create", map[string]interface{}{
@@ -226,8 +442,48 @@ func (a *API) OrderIdConvert(orderID string, isElemeOrder bool) (convertedOrderI
return "", err
}
// 查看售后订单详情
func (a *API) OrderPartrefundGet(orderID string) (orderMap map[string]interface{}, err error) {
// 查看订单列表
// https://open-be.ele.me/dev/api/doc/v3/#api-Order_Up-order_list
// page从1开始
func (a *API) OrderList(shopID string, baiduShopID int64, startTime, endTime int64, status int, page int) (listOrderInfo *ListOrderInfo, err error) {
params := a.genShopIDParams(shopID, baiduShopID, 0)
if startTime > 0 {
params["start_time"] = startTime
}
if endTime > 0 {
params["end_time"] = endTime
}
if status > 0 {
params["status"] = status
}
if page > 0 {
params["page"] = page
}
result, err := a.AccessAPI("order.list", params)
if err == nil {
err = utils.Map2StructByJson(result.Data, &listOrderInfo, true)
}
return listOrderInfo, err
}
func (a *API) OrderListAll(shopID string, baiduShopID int64, startTime, endTime int64, status int) (listOrder []*ListOrderItemInfo, err error) {
page := 1
for {
result, err := a.OrderList(shopID, baiduShopID, startTime, endTime, status, page)
if err != nil {
return nil, err
}
listOrder = append(listOrder, result.List...)
if result.Page == result.Pages {
break
}
page++
}
return listOrder, nil
}
// 查看部分退款订单详情
func (a *API) OrderPartRefundGet(orderID string) (orderMap map[string]interface{}, err error) {
result, err := a.AccessAPI("order.partrefund.get", map[string]interface{}{
"order_id": orderID,
})
@@ -237,6 +493,14 @@ func (a *API) OrderPartrefundGet(orderID string) (orderMap map[string]interface{
return nil, err
}
// func (a *API) OrderPartRefundGet2(orderID string) (partRefundInfo *CBPartRefundInfo, err error) {
// result, err := a.OrderPartRefundGet(orderID)
// if err == nil {
// err = utils.Map2StructByJson(result, &partRefundInfo, true)
// }
// return partRefundInfo, err
// }
func (a *API) SmartOrderIdConvert(orderID string) (convertedOrderID string, err error) {
return a.OrderIdConvert(orderID, isOrderIDEleme(orderID))
}

View File

@@ -7,7 +7,7 @@ import (
)
func TestOrderGet(t *testing.T) {
result, err := api.OrderGet("1555731848221773250")
result, err := api.OrderGet("1560213937227424971")
if err != nil {
t.Fatal(err)
} else {
@@ -15,8 +15,44 @@ func TestOrderGet(t *testing.T) {
}
}
func TestOrderGet2(t *testing.T) {
result, err := api.OrderGet2("1560213937227424971")
if err != nil {
t.Fatal(err)
} else {
t.Log(utils.Format4Output(result, false))
}
}
func TestOrderList(t *testing.T) {
result, err := api.OrderList("", 0, 0, 0, 0, 0)
if err != nil {
t.Fatal(err)
} else {
t.Log(utils.Format4Output(result, false))
}
}
func TestOrderPartRefundGet(t *testing.T) {
result, err := api.OrderPartRefundGet("1557459492221457830")
if err != nil {
t.Fatal(err)
} else {
t.Log(utils.Format4Output(result, false))
}
}
// func TestOrderPartRefundGet2(t *testing.T) {
// result, err := api.OrderPartRefundGet2("1557459492221457830")
// if err != nil {
// t.Fatal(err)
// } else {
// t.Log(utils.Format4Output(result, false))
// }
// }
func TestOrderCallDelivery(t *testing.T) {
err := api.OrderCallDelivery("15381031350154")
err := api.OrderCallDelivery("1556617836226053651")
if err != nil {
t.Fatal(err)
}
@@ -59,3 +95,15 @@ func TestSmartOrderIdConvert(t *testing.T) {
}
t.Log(result)
}
func TestOrderPartRefund(t *testing.T) {
err := api.OrderPartRefund("3035779376398878822", []*RefundSku{
&RefundSku{
CustomeSkuID: "",
Number: "1",
},
})
if err != nil {
t.Fatal(err)
}
}

View File

@@ -37,6 +37,7 @@ const (
KeyCustomSkuID = "custom_sku_id"
KeySkuID = "sku_id"
KeyUPC = "upc"
KeyName = "name"
KeyPhone = "phone"
@@ -47,22 +48,23 @@ const (
CoordTypeAutonavi = "amap"
)
// https://open-be.ele.me/dev/api/doc/v3/#api-Shop-shop_get
const (
DeliveryTypeElmNone = 0
DeliveryTypeElmXingHuoTrial = 1
DeliveryTypeElmXingHuo = 4
DeliveryTypeElmFengNiaoZS = 5
DeliveryTypeElmFengNiaoZSKA = 6
DeliveryTypeElmFengNiaoKS = 9
DeliveryTypeElmXingHuoZBTrial = 10
DeliveryTypeElmXingHuoZB = 11
DeliveryTypeElmNewRetail = 12
DeliveryTypeElmEBase = 13
DeliveryTypeElmEPeiSong = 14
DeliveryTypeElmFengNiaoHybrid = 15
DeliveryTypeElmFengNiaoNiubee = 16
DeliveryTypeElmXingHuoKA = 17
DeliveryTypeElmXingHuoZBKA = 18
DeliveryTypeElmNone = 0 // 暂无
DeliveryTypeElmXingHuoTrial = 1 // 星火计划(试用)
DeliveryTypeElmXingHuo = 4 // 星火计划
DeliveryTypeElmFengNiaoZS = 5 // 蜂鸟专送
DeliveryTypeElmFengNiaoZSKA = 6 // 蜂鸟专送,KA
DeliveryTypeElmFengNiaoKS = 9 // 蜂鸟快送
DeliveryTypeElmXingHuoZBTrial = 10 // 星火众包(试用)
DeliveryTypeElmXingHuoZB = 11 // 星火众包
DeliveryTypeElmNewRetail = 12 // 新零售
DeliveryTypeElmEBase = 13 // e基础
DeliveryTypeElmEPeiSong = 14 // e配送
DeliveryTypeElmFengNiaoHybrid = 15 // 蜂鸟混合送
DeliveryTypeElmFengNiaoNiubee = 16 // 蜂鸟质选
DeliveryTypeElmXingHuoKA = 17 // 星火计划KA
DeliveryTypeElmXingHuoZBKA = 18 // 星火众包KA
)
type ShopInfo struct {

View File

@@ -1,6 +1,7 @@
package ebaiapi
import (
"fmt"
"regexp"
"strings"
@@ -8,8 +9,9 @@ import (
)
const (
SkuStatusOnline = 1
SkuStatusOffline = 0
SkuStatusOnline = 1 // 为上架
SkuStatusOffline = 0 // 为下架
SkuStatusDeleted = 2 // 为删除
)
const (
@@ -17,7 +19,8 @@ const (
UPCTypePrivate = 0
)
const (
MaxLeftNum = 99999
MaxLeftNum = 99999
MaxSkuNameByteCount = 100 // skuname的最大字节数注意不是字符数超长饿百会报错{"data":"","errno":20200,"error":"invalid param:[name]...
)
type CategoryInfo struct {
@@ -28,17 +31,87 @@ type CategoryInfo struct {
Level int `json:"level"`
}
type SkuListParams struct {
Page int `json:"page,omitempty"`
PageSize int `json:"pagesize,omitempty"`
Upc string `json:"upc,omitempty"`
SkuID int64 `json:"sku_id,omitempty"`
CustomSkuID string `json:"custom_sku_id,omitempty"`
UpcType int `json:"upc_type,omitempty"`
GetUncate int `json:"get_uncate,omitempty"`
Delete int `json:"delete,omitempty"`
Enabled int `json:"enabled,omitempty"`
StartTime int `json:"start_time,omitempty"`
EndTime int `json:"end_time,omitempty"`
}
type SkuPhotoInfo struct {
IsMaster int `json:"is_master"`
URL string `json:"url"`
}
type SkuCustomCatInfo struct {
CustomCatID string `json:"custom_cat_id"`
CustomCatName string `json:"custom_cat_name"`
}
type SkuInfo struct {
CustomCatIDs string `json:"custom_cat_ids"`
CustomCatList []*SkuCustomCatInfo `json:"custom_cat_list"`
CustomSkuID string `json:"custom_sku_id"`
IsInActivity int `json:"is_in_activity"`
LeftNum int `json:"left_num"`
MarketPrice int `json:"market_price"`
Minimum int `json:"minimum"`
Name string `json:"name"`
NeedIce int `json:"need_ice"`
Photos []*SkuPhotoInfo `json:"photos"`
PreminusWeight int `json:"preminus_weight"`
PreparationTime string `json:"preparation_time"`
ProductionAddr1 string `json:"production_addr1"`
ProductionAddr2 string `json:"production_addr2"`
ProductionAddr3 string `json:"production_addr3"`
Rtf string `json:"rtf"`
SalePrice int64 `json:"sale_price"`
SaleStep string `json:"sale_step"`
SaleUnit string `json:"sale_unit"`
ShelfNumber string `json:"shelf_number"`
SkuID int64 `json:"sku_id"`
// SkuProperty []interface{} `json:"sku_property"`
Status int `json:"status"`
Summary string `json:"summary"`
Upc string `json:"upc"`
UpcType string `json:"upc_type"`
UpdateTime string `json:"update_time"`
Weight int `json:"weight"`
WeightFlag int `json:"weight_flag"`
}
type PageDataInfo struct {
Total int
Page int
Pages int
List []map[string]interface{}
Total int `json:"Total"`
Page int `json:"Page"`
Pages int `json:"Pages"`
List []*SkuInfo `json:"List"`
}
var (
skuExistReg = regexp.MustCompile(`\s?,\s?sku_id:(\d+)`)
)
func genSkuIDParams(skuID int64, customSkuID, upc string) map[string]interface{} {
params := map[string]interface{}{}
if skuID != 0 {
params[KeySkuID] = skuID
} else if customSkuID != "" {
params[KeyCustomSkuID] = customSkuID
} else if upc != "" {
params[KeyUPC] = upc
} else {
panic("skuID, customSkuID and upc are all no value!")
}
return params
}
// category相关的函数shop_custom_id可重
func (a *API) ShopCategoryCreate(shopID string, parentID int64, name string, rank int) (catID int64, err error) {
@@ -99,21 +172,14 @@ func (a *API) SkuGetItemsByCategoryId(shopID string, categoryID int64) (skus []m
return nil, err
}
func (a *API) SkuList(shopID string, params map[string]interface{}) (skuInfo *PageDataInfo, err error) {
defParams := map[string]interface{}{
KeyShopID: shopID,
}
result, err := a.AccessAPI("sku.list", utils.MergeMaps(params, defParams))
func (a *API) SkuList(shopID string, params *SkuListParams) (skuInfo *PageDataInfo, err error) {
paramMap := utils.Struct2FlatMap(params)
paramMap[KeyShopID] = shopID
result, err := a.AccessAPI("sku.list", paramMap)
if err == nil {
data := result.Data.(map[string]interface{})
return &PageDataInfo{
Total: int(utils.MustInterface2Int64(data["total"])),
Page: int(utils.MustInterface2Int64(data["page"])),
Pages: int(utils.MustInterface2Int64(data["pages"])),
List: utils.Slice2MapSlice(data["list"].([]interface{})),
}, nil
err = utils.Map2StructByJson(result.Data, &skuInfo, true)
}
return nil, err
return skuInfo, err
}
func (a *API) SkuCreate(shopID string, customSkuID int, params map[string]interface{}) (skuID int64, err error) {
@@ -168,9 +234,6 @@ func (a *API) SkuDelete(shopID, skuIDsStr string) (err error) {
KeySkuID: skuIDsStr,
}
_, err = a.AccessAPI("sku.delete", params)
if err == nil {
return nil
}
return err
}
@@ -180,9 +243,6 @@ func (a *API) SkuDeleteByCustomIDs(shopID, customSkuIDsStr string) (err error) {
KeyCustomSkuID: customSkuIDsStr,
}
_, err = a.AccessAPI("sku.delete", params)
if err == nil {
return nil
}
return err
}
@@ -192,9 +252,13 @@ func (a *API) SkuOnline(shopID, skuIDsStr string) (err error) {
KeySkuID: skuIDsStr,
}
_, err = a.AccessAPI("sku.online", params)
if err == nil {
return nil
}
return err
}
func (a *API) SkuOnlineOne(shopID string, skuID int64, customSkuID, upc string) (err error) {
params := genSkuIDParams(skuID, customSkuID, upc)
params[KeyShopID] = shopID
_, err = a.AccessAPI("sku.online.one", params)
return err
}
@@ -204,9 +268,41 @@ func (a *API) SkuOffline(shopID, skuIDsStr string) (err error) {
KeySkuID: skuIDsStr,
}
_, err = a.AccessAPI("sku.offline", params)
if err == nil {
return nil
return err
}
func (a *API) SkuOfflineOne(shopID string, skuID int64, customSkuID, upc string) (err error) {
params := genSkuIDParams(skuID, customSkuID, upc)
params[KeyShopID] = shopID
_, err = a.AccessAPI("sku.offline.one", params)
return err
}
func genSkuPriceParams(skuPriceStr, customSkuPriceStr, upcPriceStr string) map[string]interface{} {
params := map[string]interface{}{}
if skuPriceStr != "" {
params["skuid_price"] = skuPriceStr
} else if skuPriceStr != "" {
params["custom_sku_id"] = customSkuPriceStr
} else if upcPriceStr != "" {
params["upc_price"] = upcPriceStr
} else {
panic("skuPriceStr, customSkuPriceStr and upcPriceStr are all empty!")
}
return params
}
func (a *API) SkuPriceUpdateBatch(shopID, skuPriceStr, customSkuPriceStr, upcPriceStr string) (err error) {
params := genSkuPriceParams(skuPriceStr, customSkuPriceStr, upcPriceStr)
params[KeyShopID] = shopID
_, err = a.AccessAPI("sku.price.update.batch", params)
return err
}
func (a *API) SkuPriceUpdateOne(shopID, skuPriceStr, customSkuPriceStr, upcPriceStr string) (err error) {
params := genSkuPriceParams(skuPriceStr, customSkuPriceStr, upcPriceStr)
params[KeyShopID] = shopID
_, err = a.AccessAPI("sku.price.update.one", params)
return err
}
@@ -217,12 +313,30 @@ func (a *API) SkuShopCategoryMap(shopID string, skuID int64, categoryIDsStr stri
"category_id": categoryIDsStr,
}
_, err = a.AccessAPI("sku.shop.category.map", params)
if err == nil {
return nil
}
return err
}
// 饿百的这个API有点怪虽然有shopID参数但返回的链接可以跨店使用
func (a *API) SkuUploadRTF(shopID, rtfDetail string) (rtfURL string, err error) {
params := map[string]interface{}{
KeyShopID: shopID,
"rtf_detail": rtfDetail,
}
result, err := a.AccessAPI("sku.uploadrtf", params)
if err == nil {
rtfURL = utils.Interface2String(result.Data.(map[string]interface{})["url"])
}
return rtfURL, err
}
func BuildRFTFromImgs(imgList ...string) string {
imgList2 := make([]string, len(imgList))
for index, img := range imgList {
imgList2[index] = fmt.Sprintf(`<img align="absmiddle" src="%s" alt="%s" />`, img, img)
}
return strings.Join(imgList2, "\n")
}
//
func interface2CatList(data interface{}, level int) (cats []*CategoryInfo) {
maps, ok := data.([]interface{})

View File

@@ -48,9 +48,8 @@ func TestSkuGetItemsByCategoryId(t *testing.T) {
}
func TestSkuList(t *testing.T) {
result, err := api.SkuList(testShopID, map[string]interface{}{
// KeySkuID: 153879464137191,
// "delete": 1,
result, err := api.SkuList(testShopID, &SkuListParams{
SkuID: 15579787500720732,
})
if err != nil {
t.Fatal(err)
@@ -59,6 +58,16 @@ func TestSkuList(t *testing.T) {
}
}
func TestSkuUploadRTF(t *testing.T) {
rtfDetail := BuildRFTFromImgs("https://image.jxc4.com/sijidou.jpg")
t.Log(rtfDetail)
result, err := api.SkuUploadRTF("2", rtfDetail)
if err != nil {
t.Fatal(err)
}
t.Log(result)
}
func TestSkuCreate(t *testing.T) {
result, err := api.SkuCreate(testShopID, 17, map[string]interface{}{
"name": "测试商品",
@@ -83,6 +92,21 @@ func TestSkuCreate(t *testing.T) {
}
}
func TestSkuUpdate(t *testing.T) {
// 15579787500720732 高级
result, err := api.SkuUpdate("2", 1557043939079105, map[string]interface{}{
// "name": "高级商品2015a333约1100g/份",
// "rtf": "http://www.rosy.net.cn/rtf.html",
"shelf_number": 12,
})
if err != nil {
t.Fatal(err)
} else {
t.Log(utils.Format4Output(result, false))
}
}
func TestSkuDelete(t *testing.T) {
err := api.SkuDelete(testShopID, "153922044227304")
if err != nil {

View File

@@ -3,6 +3,7 @@ package ebaiapi
import (
"fmt"
"net/http"
"strings"
"time"
"git.rosy.net.cn/baseapi"
@@ -11,8 +12,9 @@ import (
)
const (
storeURL = "https://be.ele.me"
getStoreURL = "https://newretail.ele.me"
storeURL = "https://be.ele.me"
getStoreURL = "https://newretail.ele.me"
swithShopURL = "crm/manager/switchshop"
)
const (
@@ -32,6 +34,131 @@ const (
CommentContentNoContent = 0
)
type PageShopUserInfo struct {
CategoryID string `json:"category_id"`
CityID string `json:"city_id"`
CityName string `json:"city_name"`
DeliveryParty string `json:"delivery_party"`
EleID string `json:"ele_id"`
EleSoaToken string `json:"ele_soa_token"`
IsAPI int `json:"is_api"`
IsStore interface{} `json:"is_store"`
MerchantID string `json:"merchant_id"`
MerchantName string `json:"merchant_name"`
Role struct {
Ename string `json:"ename"`
Name string `json:"name"`
ShopRoleID int64 `json:"shop_role_id"`
} `json:"role"`
SalesID string `json:"sales_id"`
SalesName string `json:"sales_name"`
ServicePackage struct {
BaiduSign int `json:"baiduSign"`
EleSign int `json:"eleSign"`
} `json:"service_package"`
ShopInfo struct {
AreaID string `json:"area_id"`
BaiduBusinessState string `json:"baidu_business_state"`
BaiduOnlineStatus string `json:"baidu_online_status"`
BaiduTakeoutLogo string `json:"baidu_takeout_logo"`
CategoryID string `json:"category_id"`
CategoryIds string `json:"category_ids"`
CategoryName string `json:"category_name"`
County string `json:"county"`
CreateType string `json:"create_type"`
CreatedAt string `json:"created_at"`
CrmOncallType int `json:"crm_oncall_type"`
EffectiveAt string `json:"effective_at"`
EleBusinessState string `json:"ele_business_state"`
EleDeliveryParty string `json:"ele_delivery_party"`
EleID string `json:"ele_id"`
EleOnlineStatus string `json:"ele_online_status"`
EleShopLogo interface{} `json:"ele_shop_logo"`
IsSignZhongbao string `json:"is_sign_zhongbao"`
OnlineStatus string `json:"online_status"`
Phone string `json:"phone"`
ServStatus string `json:"serv_status"`
ShopTransactionOpen int `json:"shop_transaction_open"`
SourceName string `json:"source_name"`
SupplierID string `json:"supplier_id"`
TakeoutBoxPrice string `json:"takeout_box_price"`
TakeoutDispatchTime []struct {
End string `json:"end"`
Start string `json:"start"`
} `json:"takeout_dispatch_time"`
TakeoutOncallType string `json:"takeout_oncall_type"`
TakeoutOpenTime []struct {
End string `json:"end"`
Start string `json:"start"`
} `json:"takeout_open_time"`
TakeoutServicePhone string `json:"takeout_service_phone"`
TakeoutShopLogo string `json:"takeout_shop_logo"`
TransferStatus string `json:"transfer_status"`
} `json:"shop_info"`
ShopRole int `json:"shop_role"`
ShopUserID string `json:"shop_user_id"`
SwitchFromSupplier int `json:"switch_from_supplier"`
UserName string `json:"user_name"`
UserPhone string `json:"user_phone"`
YellowPrompt struct {
Capacity int `json:"capacity"`
Complete int `json:"complete"`
Display bool `json:"display"`
Msg string `json:"msg"`
MsgCount int `json:"msg_count"`
MsgList []interface{} `json:"msg_list"`
Online int `json:"online"`
PcURL string `json:"pc_url"`
TransferStatus string `json:"transfer_status"`
Type int `json:"type"`
URL string `json:"url"`
} `json:"yellow_prompt"`
}
type PageShopHealthInfo struct {
MerchantID string `json:"merchant_id"`
MerchantName string `json:"merchant_name"`
BadOrderRate string `json:"bad_order_rate"`
Category2 string `json:"category_2"`
Data string `json:"data"`
EndTime string `json:"endTime"`
HasPhotoRate string `json:"has_photo_rate"`
Hours string `json:"hours"`
ID string `json:"id"`
IsButie string `json:"is_butie"`
IsCp string `json:"is_cp"`
IsDeliverFee string `json:"is_deliver_fee"`
IsGood string `json:"is_good"`
IsHealthy string `json:"is_healthy"`
IsJiedan string `json:"is_jiedan"`
IsManjian string `json:"is_manjian"`
IsQisong string `json:"is_qisong"`
IsShopNew string `json:"is_shop_new"`
IsSku string `json:"is_sku"`
IsYinye string `json:"is_yinye"`
IsYoutu string `json:"is_youtu"`
IsZs string `json:"is_zs"`
MinDeliveryFee string `json:"min_delivery_fee"`
MinDeliveryPrice string `json:"min_delivery_price"`
RestaurantSubsidy string `json:"restaurant_subsidy"`
ShopCategory string `json:"shop_category"`
ShopID string `json:"shop_id"`
SkuNum string `json:"sku_num"`
StartTime string `json:"startTime"`
TargetButie string `json:"target_butie"`
TargetCp string `json:"target_cp"`
TargetDeliverFee string `json:"target_deliver_fee"`
TargetJiedan string `json:"target_jiedan"`
TargetQisong string `json:"target_qisong"`
TargetSku string `json:"target_sku"`
TargetYinye string `json:"target_yinye"`
TargetYoutu string `json:"target_youtu"`
UnvalidOrderNum string `json:"unvalid_order_num"`
UpdateTime string `json:"update_time"`
Week string `json:"week"`
}
func (a *API) SetStoreCookie(key, value string) {
a.locker.Lock()
defer a.locker.Unlock()
@@ -44,7 +171,7 @@ func (a *API) GetStoreCookie(key string) string {
return a.storeCookies[key]
}
func (a *API) AccessStorePage(subURL string) (retVal map[string]interface{}, err error) {
func (a *API) AccessStorePage2(subURL string, params map[string]interface{}, cookies map[string]string) (retVal map[string]interface{}, err error) {
a.locker.RLock()
storeCookieLen := len(a.storeCookies)
a.locker.RUnlock()
@@ -53,11 +180,15 @@ func (a *API) AccessStorePage(subURL string) (retVal map[string]interface{}, err
}
err = platformapi.AccessPlatformAPIWithRetry(a.client,
func() *http.Request {
var request *http.Request
fullURL := utils.GenerateGetURL(storeURL, subURL, nil)
// baseapi.SugarLogger.Debug(fullURL)
request, _ := http.NewRequest(http.MethodGet, fullURL, nil)
if err != nil {
return nil
if params == nil {
// baseapi.SugarLogger.Debug(fullURL)
request, _ = http.NewRequest(http.MethodGet, fullURL, nil)
} else {
request, _ = http.NewRequest(http.MethodPost, fullURL, strings.NewReader(utils.Map2URLValues(params).Encode()))
request.Header.Set("charset", "UTF-8")
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
a.locker.RLock()
for k, v := range a.storeCookies {
@@ -66,15 +197,30 @@ func (a *API) AccessStorePage(subURL string) (retVal map[string]interface{}, err
Value: v,
})
}
for k, v := range cookies {
request.AddCookie(&http.Cookie{
Name: k,
Value: v,
})
}
a.locker.RUnlock()
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (errLevel string, err error) {
func(response *http.Response, jsonResult1 map[string]interface{}) (errLevel string, err error) {
retVal = jsonResult1
code := int(utils.MustInterface2Int64(jsonResult1["errno"]))
if code == ResponseCodeSuccess {
retVal = jsonResult1["data"].(map[string]interface{})
if subURL == swithShopURL {
for _, v := range response.Cookies() {
if v.Name == "SWITCH_SHOP" {
retVal = utils.Struct2FlatMap(v)
break
}
}
} else {
retVal, _ = jsonResult1["data"].(map[string]interface{})
}
return platformapi.ErrLevelSuccess, nil
}
newErr := utils.NewErrorIntCode(jsonResult1["errmsg"].(string), code)
@@ -87,6 +233,10 @@ func (a *API) AccessStorePage(subURL string) (retVal map[string]interface{}, err
return retVal, err
}
func (a *API) AccessStorePage(subURL string, params map[string]interface{}) (retVal map[string]interface{}, err error) {
return a.AccessStorePage2(subURL, params, nil)
}
func (a *API) GetRealMobile4Order(orderId string) (mobile string, err error) {
retVal, err := a.GetStoreOrderInfo(orderId)
if err == nil {
@@ -96,7 +246,7 @@ func (a *API) GetRealMobile4Order(orderId string) (mobile string, err error) {
}
func (a *API) GetStoreOrderInfo(orderId string) (storeOrderInfo map[string]interface{}, err error) {
retVal, err := a.AccessStorePage(fmt.Sprintf("crm/orderlist?keyword=%s", orderId))
retVal, err := a.AccessStorePage(fmt.Sprintf("crm/orderlist?keyword=%s", orderId), nil)
// baseapi.SugarLogger.Debug(utils.Format4Output(retVal, false))
if err == nil {
resultList := retVal["order_list"].([]interface{})
@@ -123,7 +273,7 @@ func (a *API) GetStoreOrderInfoList(fromTime, toTime string, shopID string, orde
}
fixedURL := fmt.Sprintf(urlTemplate, params...)
for {
retVal, err2 := a.AccessStorePage(fixedURL + "&page=" + utils.Int2Str(pageNo))
retVal, err2 := a.AccessStorePage(fixedURL+"&page="+utils.Int2Str(pageNo), nil)
// baseapi.SugarLogger.Debug(utils.Format4Output(retVal, false))
if err = err2; err == nil {
resultList := retVal["order_list"].([]interface{})
@@ -173,7 +323,7 @@ func (a *API) getCommentList(isElm bool, fromTime, toTime time.Time, shopID, sup
}
fixedURL := fmt.Sprintf(urlTemplate, params...)
for {
retVal, err2 := a.AccessStorePage(fixedURL + "&page_num=" + utils.Int2Str(pageNo))
retVal, err2 := a.AccessStorePage(fixedURL+"&page_num="+utils.Int2Str(pageNo), nil)
if err = err2; err == nil {
for _, comment := range retVal["comment_list"].([]interface{}) {
commentMap := comment.(map[string]interface{})
@@ -213,7 +363,7 @@ func (a *API) PageGetSkuList(baiduShopID int64) (skuList []map[string]interface{
}
fixedURL := fmt.Sprintf(urlTemplate, params...)
for {
retVal, err2 := a.AccessStorePage(fixedURL + "&curpage=" + utils.Int2Str(pageNo))
retVal, err2 := a.AccessStorePage(fixedURL+"&curpage="+utils.Int2Str(pageNo), nil)
if err = err2; err == nil {
for _, sku := range retVal["sku_list"].([]interface{}) {
skuList = append(skuList, sku.(map[string]interface{}))
@@ -236,7 +386,7 @@ func (a *API) PageGetCustomSkuList(baiduShopID int64, customCatID int64) (skuLis
customCatID,
}
fixedURL := fmt.Sprintf(urlTemplate, params...)
retVal, err := a.AccessStorePage(fixedURL)
retVal, err := a.AccessStorePage(fixedURL, nil)
if err == nil {
return utils.Slice2MapSlice(retVal["sku_list"].([]interface{})), nil
}
@@ -249,7 +399,7 @@ func (a *API) PageGetCustomCatList(baiduShopID int64) (catList []map[string]inte
baiduShopID,
}
fixedURL := fmt.Sprintf(urlTemplate, params...)
retVal, err := a.AccessStorePage(fixedURL)
retVal, err := a.AccessStorePage(fixedURL, nil)
if err == nil {
return utils.Slice2MapSlice(retVal["cat_list"].([]interface{})), nil
}
@@ -257,15 +407,17 @@ func (a *API) PageGetCustomCatList(baiduShopID int64) (catList []map[string]inte
}
func (a *API) GetStoreInfo(storeId string) (storeInfo map[string]interface{}, err error) {
retVal, err := a.AccessStorePage2(storeId)
retVal, err := a.AccessStorePageNoCookie(fmt.Sprintf("newretail/shop/getshopinfo?&lat=0&lng=0&shop_id=%s", storeId))
if err != nil {
return nil, err
}
if retVal != nil {
retVal["shop_id"] = storeId
}
return retVal, err
}
func (a *API) AccessStorePage2(storeId string) (retVal map[string]interface{}, err error) {
subURL := fmt.Sprintf("newretail/shop/getshopinfo?&lat=0&lng=0&shop_id=%s", storeId)
func (a *API) AccessStorePageNoCookie(subURL string) (retVal map[string]interface{}, err error) {
err = platformapi.AccessPlatformAPIWithRetry(a.client,
func() *http.Request {
fullURL := utils.GenerateGetURL(getStoreURL, subURL, nil)
@@ -276,16 +428,12 @@ func (a *API) AccessStorePage2(storeId string) (retVal map[string]interface{}, e
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (errLevel string, err error) {
func(response *http.Response, jsonResult1 map[string]interface{}) (errLevel string, err error) {
code := int(utils.MustInterface2Int64(jsonResult1["error_no"]))
if code == ResponseCodeSuccess {
retVal = jsonResult1["result"].(map[string]interface{})
retVal["shop_id"] = storeId
return platformapi.ErrLevelSuccess, nil
} else if code == GetStoreSuccessButUnderfind {
retVal = make(map[string]interface{})
retVal["shop_id"] = storeId
retVal["storeIsUnderfind"] = 1
return platformapi.ErrLevelSuccess, nil
}
newErr := utils.NewErrorIntCode(jsonResult1["error_msg"].(string), code)
@@ -295,29 +443,61 @@ func (a *API) AccessStorePage2(storeId string) (retVal map[string]interface{}, e
}
func (a *API) GetStoreList(lng string, lat string) (retVal map[string]interface{}, err error) {
retVal, err = a.AccessStorePage3(fmt.Sprintf("/newretail/main/shoplist?channel=kitchen&pn=1&rn=999&lng=%s&lat=%s", lng, lat))
retVal, err = a.AccessStorePageNoCookie(fmt.Sprintf("/newretail/main/shoplist?channel=kitchen&pn=1&rn=999&lng=%s&lat=%s", lng, lat))
return retVal, err
}
func (a *API) AccessStorePage3(subURL string) (retVal map[string]interface{}, err error) {
err = platformapi.AccessPlatformAPIWithRetry(a.client,
func() *http.Request {
fullURL := utils.GenerateGetURL(getStoreURL, subURL, nil)
request, _ := http.NewRequest(http.MethodGet, fullURL, nil)
if err != nil {
return nil
}
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (errLevel string, err error) {
code := int(utils.MustInterface2Int64(jsonResult1["error_no"]))
if code == ResponseCodeSuccess {
retVal = jsonResult1["result"].(map[string]interface{})
return platformapi.ErrLevelSuccess, nil
}
newErr := utils.NewErrorIntCode(jsonResult1["error_msg"].(string), code)
return platformapi.ErrLevelCodeIsNotOK, newErr
})
return retVal, err
func (a *API) SwitchShop(baiduShopID int64) (switchShopCookie string, err error) {
result, err := a.AccessStorePage("crm/manager/switchshop", map[string]interface{}{
"switch_shop_id": baiduShopID,
})
if err == nil {
switchShopCookie = utils.Interface2String(result["Value"])
}
return switchShopCookie, err
}
func (a *API) GetShopUserInfo2(switchShopCookie string) (shopUserInfo *PageShopUserInfo, err error) {
shopInfo, err := a.AccessStorePage2("crm/account/getshopuserinfo", nil, map[string]string{
"SWITCH_SHOP": switchShopCookie,
})
if err == nil {
err = utils.Map2StructByJson(shopInfo, &shopUserInfo, true)
}
return shopUserInfo, err
}
func (a *API) GetShopUserInfo(baiduShopID int64) (shopUserInfo *PageShopUserInfo, err error) {
switchShopCookie, err := a.SwitchShop(baiduShopID)
if err != nil {
return nil, err
}
return a.GetShopUserInfo2(switchShopCookie)
}
func (a *API) GetShopHealthByDetail2(switchShopCookie string) (shopHealthDetail *PageShopHealthInfo, err error) {
shopInfo, err := a.GetShopUserInfo2(switchShopCookie)
if err != nil {
return nil, err
}
result, err := a.AccessStorePage2("crm/getshophealthydetail", map[string]interface{}{
"shop_id": shopInfo.EleID,
}, map[string]string{
"SWITCH_SHOP": switchShopCookie,
})
if err == nil {
if err = utils.Map2StructByJson(result, &shopHealthDetail, true); err == nil {
shopHealthDetail.MerchantID = shopInfo.MerchantID
shopHealthDetail.MerchantName = shopInfo.MerchantName
}
}
return shopHealthDetail, err
}
func (a *API) GetShopHealthByDetail(baiduShopID int64) (shopHealthDetail *PageShopHealthInfo, err error) {
switchShopCookie, err := a.SwitchShop(baiduShopID)
if err != nil {
return nil, err
}
return a.GetShopHealthByDetail2(switchShopCookie)
}

View File

@@ -81,3 +81,43 @@ func TestPageGetCustomCatList(t *testing.T) {
baseapi.SugarLogger.Debug(utils.Format4Output(catList, false))
}
}
func TestSwitchShop(t *testing.T) {
cookie, err := api.SwitchShop(2233065941)
if err != nil {
t.Fatal(err)
}
t.Log(cookie)
}
func TestGetShopUserInfo(t *testing.T) {
result, err := api.GetShopUserInfo(32267034127)
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}
func TestGetShopHealthByDetail(t *testing.T) {
result, err := api.GetShopHealthByDetail(32267034127)
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}
func TestGetStoreList(t *testing.T) {
result, err := api.GetStoreList("104.057218", "30.6949")
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}
func TestGetStoreInfo(t *testing.T) {
result, err := api.GetStoreInfo("170879219")
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}

View File

@@ -159,7 +159,7 @@ func (a *API) AccessAPI(action string, params map[string]interface{}) (retVal *R
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (result string, err error) {
func(response *http.Response, jsonResult1 map[string]interface{}) (result string, err error) {
resultError, _ := jsonResult1["error"].(map[string]interface{})
retVal = &ResponseResult{
ID: jsonResult1["id"].(string),
@@ -216,7 +216,7 @@ func (a *API) AcccessAPI2(baseURL string, params map[string]interface{}, method
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (result string, err error) {
func(response *http.Response, jsonResult1 map[string]interface{}) (result string, err error) {
retVal = jsonResult1
return platformapi.ErrLevelSuccess, nil
})

View File

@@ -121,7 +121,7 @@ func (a *API) AccessAPI(apiName string, apiParams map[string]interface{}) (retVa
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (errLevel string, err error) {
func(response *http.Response, jsonResult1 map[string]interface{}) (errLevel string, err error) {
code := int(utils.Interface2Int64WithDefault(jsonResult1["ret"], ResponseCodeSuccess))
if code == ResponseCodeSuccess {
retVal = jsonResult1["data"]

View File

@@ -2,19 +2,102 @@ package jdapi
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"git.rosy.net.cn/baseapi"
"git.rosy.net.cn/baseapi/platformapi"
"git.rosy.net.cn/baseapi/utils"
)
const (
CallbackPrefix = "/djsw/"
)
// 如下的常量其实都是京东回调消息的
const (
OrderStatusAddComment = "12001"
OrderStatusModifyComment = "12006"
OrderStatusTipChanged = "12008"
OrderStatusPurchased = "41000" // 也即待处理JD的消息很怪新订单消息发过来是32000但如果不是自动接单的去查却是41000接单后才变为32000
OrderStatusPayed = "31020" // 已付款
StatusIDNewOrder = "32000"
OrderStatusWaitOutStore = "32000"
OrderStatusAdjust = "33080"
StatusIDWaitOutStore = "32001"
OrderStatusFinishedPickup = "2"
OrderStatusDelivering = "33040"
OrderStatusDelivered = "33060"
OrderStatusFinished = "90000"
OrderStatusCanceled = "20020"
OrderStatusUserApplyCancel = "20030" // 这个其实不是一个状态,是一个动作
OrderStatusLocked = "20010"
OrderStatusUnlocked = "20050"
OrderStatusInfoChanged = "1" // 订单信息变更消息
OrderStatusPayFinishedSettle = "330901" // 订单支付完成应结
OrderStatusAdjustSettle = "330902" // 订单调整后应结
OrderStatusSwitch2SelfSettle = "330903" // 订单众包配送转自送后应结
)
const (
StatusIDAddStore = "12003" // 新增门店消息
StatusIDDelStore = "12004" // 删除门店消息
StatusIDUpdateStore = "12009" // 修改门店消息
)
const (
AfsServiceStateWaiting4Audit = "10" // 待审核
AfsServiceStateWaiting4UserFeedback = "11" // 待用户反馈
AfsServiceStateWaiting4CSFeedback = "12" // 待客服反馈
AfsServiceStateWaiting4GetGoods = "20" // 待取件
AfsServiceStateRefundProcessing = "30" // 退款处理中
AfsServiceStateWaiting4MerchantReceiveGoods = "31" // 待商家收货审核
AfsServiceStateRefundSuccess = "32" // 退款成功
AfsServiceStateRefundFailed = "33" // 退款失败
AfsServiceStateAuditRefused = "40" // 审核不通过-驳回
AfsServiceStateUserCanceled = "50" // 客户取消
AfsServiceStateMerchantFailedReceiveGoods = "60" // 商家收货审核不通过
AfsServiceStateSolved = "70" // 已解决
AfsServiceStateWaiting4DirectCompensate = "90" // 待直陪
AfsServiceStateDirectCompensate = "91" // 直赔
AfsServiceStateDirectCompensateSuccess = "92" // 直赔成功
AfsServiceStateDirectCompensateFailed = "93" // 直赔失败
AfsServiceStateWaiting4ReturnGoods = "110" // 待退货
AfsServiceStateGetGoodsSuccess = "111" // 取货成功
AfsServiceStateGettingGoods = "1101" // 取货中
AfsServiceStateGetGoods2Shop = "1111" // 退货成功-商品已送至门店
AfsServiceStateGetGoodsConfirmed = "1112" // 退货成功-商家已确认收货
AfsServiceStateGetGoodsWaiting4Refund = "112" // 退货成功-待退款
AfsServiceStateReturnGoodsFailed = "113" // 退货失败
AfsServiceStateReturnGoodsSuccess = "114" // 退货成功
)
const (
// 订单
CallbackMsgDeliveryCarrierModify = "deliveryCarrierModify" // 订单转自送消息(是指转自送成功后,用处不大)
CallbackMsgOrderAccounting = "orderAccounting" // 订单应结消息
// 账务
CallbackMsgEndOrderFinance = "endOrderFinance" // 订单金额拆分完成消息
CallbackMsgFinanceAdjustment = "financeAdjustment" // 财务调整单消息
// 售后
CallbackMsgNewApplyAfterSaleBill = "newApplyAfterSaleBill" // 新建售后单申请消息
CallbackMsgUpdateApplyAfterSaleBill = "updateApplyAfterSaleBill" // 修改售后单申请消息
CallbackMsgNewAfterSaleBill = "newAfterSaleBill" // 新建售后单消息
CallbackMsgAfterSaleBillStatus = "afterSaleBillStatus" // 售后单状态消息
)
type CallbackResponse struct {
Code string `json:"code"`
Msg string `json:"msg"`
@@ -22,7 +105,7 @@ type CallbackResponse struct {
}
type CallbackOrderMsg struct {
ID int `json:"-"` // 用于传递Jdorder的主键值减少一次读库操作
MsgURL string `json:"msgURL"`
BillID string `json:"billId"`
OutBillID string `json:"outBillId"`
StatusID string `json:"statusId"`
@@ -31,6 +114,7 @@ type CallbackOrderMsg struct {
}
type CallbackDeliveryStatusMsg struct {
MsgURL string `json:"msgURL"`
OrderID string `json:"orderId"`
DeliveryStatusTime string `json:"deliveryStatusTime"`
DeliveryManNo string `json:"deliveryManNo"`
@@ -62,12 +146,8 @@ const (
OpenSourceBatchTask = 14
)
const (
SaleBillStatusRefundSuccess = "32"
SaleBillStatusSaleReturnSuccess = "114"
)
type CallbackStoreStockMsg struct {
MsgURL string `json:"msgURL"`
StationNo string `json:"stationNo"`
SkuId int64 `json:"skuId"`
Have bool `json:"have"`
@@ -115,7 +195,12 @@ func (a *API) CheckCallbackValidation(values url.Values) (callbackResponse *Call
return nil
}
func (a *API) getCommonOrderCallbackMsg(data []byte, msg interface{}, needDecode bool) (callbackResponse *CallbackResponse) {
func (a *API) getCommonOrderCallbackMsg(request *http.Request, msg interface{}, needDecode bool) (callbackResponse *CallbackResponse) {
data, err := ioutil.ReadAll(request.Body)
if err != nil {
return Err2CallbackResponse(err, "")
}
// baseapi.SugarLogger.Debug(string(data))
result, err := utils.HTTPBody2Values(data, needDecode)
if err != nil {
return FormatErrorResponse
@@ -132,28 +217,34 @@ func (a *API) getCommonOrderCallbackMsg(data []byte, msg interface{}, needDecode
return nil
}
func (a *API) GetOrderCallbackMsg(data []byte) (msg *CallbackOrderMsg, callbackResponse *CallbackResponse) {
func (a *API) GetOrderCallbackMsg(request *http.Request) (msg *CallbackOrderMsg, callbackResponse *CallbackResponse) {
msg = new(CallbackOrderMsg)
callbackResponse = a.getCommonOrderCallbackMsg(data, msg, false)
if callbackResponse = a.getCommonOrderCallbackMsg(request, msg, false); callbackResponse == nil {
msg.MsgURL = getMsgURLFromRequest(request)
}
return msg, callbackResponse
}
func (a *API) GetOrderApplyCancelCallbackMsg(data []byte) (msg *CallbackOrderMsg, callbackResponse *CallbackResponse) {
func (a *API) GetOrderApplyCancelCallbackMsg(request *http.Request) (msg *CallbackOrderMsg, callbackResponse *CallbackResponse) {
msg = new(CallbackOrderMsg)
callbackResponse = a.getCommonOrderCallbackMsg(data, msg, true)
if callbackResponse = a.getCommonOrderCallbackMsg(request, msg, true); callbackResponse == nil {
msg.MsgURL = getMsgURLFromRequest(request)
}
return msg, callbackResponse
}
func (a *API) GetOrderDeliveryCallbackMsg(data []byte) (msg *CallbackDeliveryStatusMsg, callbackResponse *CallbackResponse) {
msg = new(CallbackDeliveryStatusMsg)
callbackResponse = a.getCommonOrderCallbackMsg(data, msg, true)
func (a *API) GetOrderDeliveryCallbackMsg(request *http.Request) (msg *CallbackDeliveryStatusMsg, callbackResponse *CallbackResponse) {
if callbackResponse = a.getCommonOrderCallbackMsg(request, &msg, true); callbackResponse == nil {
msg.MsgURL = getMsgURLFromRequest(request)
}
return msg, callbackResponse
}
func (a *API) GetStoreStockCallbackMsg(data []byte) (msg *CallbackStoreStockMsg, callbackResponse *CallbackResponse) {
func (a *API) GetStoreStockCallbackMsg(request *http.Request) (msg *CallbackStoreStockMsg, callbackResponse *CallbackResponse) {
msg = new(CallbackStoreStockMsg)
msg.MsgURL = getMsgURLFromRequest(request)
var tmpMsg map[string]interface{}
callbackResponse = a.getCommonOrderCallbackMsg(data, &tmpMsg, true)
callbackResponse = a.getCommonOrderCallbackMsg(request, &tmpMsg, true)
if callbackResponse == nil {
msg.StationNo = utils.Interface2String(tmpMsg["stationNo"])
msg.SkuId = utils.Str2Int64(utils.Interface2String(tmpMsg["skuId"]))
@@ -161,12 +252,15 @@ func (a *API) GetStoreStockCallbackMsg(data []byte) (msg *CallbackStoreStockMsg,
msg.OperPin = utils.Interface2String(tmpMsg["operPin"])
msg.OperTime = utils.Str2Int64(utils.Interface2String(tmpMsg["operTime"]))
msg.OperSource = int(utils.Str2Int64((utils.Interface2String(tmpMsg["operSource"]))))
have := utils.Interface2String(tmpMsg["have"])
if have == "true" {
msg.Have = true
} else {
msg.Have = false
}
msg.Have = utils.Interface2String(tmpMsg["have"]) == "true"
}
return msg, callbackResponse
}
func getMsgURLFromRequest(request *http.Request) (msgURL string) {
index := strings.Index(request.URL.Path, CallbackPrefix)
if index >= 0 {
msgURL = request.URL.Path[index+len(CallbackPrefix):]
}
return msgURL
}

View File

@@ -184,7 +184,7 @@ func (a *API) AccessAPI(apiStr string, jdParams map[string]interface{}) (retVal
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (errLevel string, err error) {
func(response *http.Response, jsonResult1 map[string]interface{}) (errLevel string, err error) {
code := jsonResult1["code"].(string)
if code == ResponseCodeSuccess {
retVal = jsonResult1

View File

@@ -27,7 +27,7 @@ func init() {
// 天天果园
// api = New("84541069-fbe2-424b-b625-9b2ba1d4c9e6", "5d5577a2506f41b8b4ec520ba83490f5", "0b01b9eeb15b41dab1c3d05d95c17a26")
api.SetStoreCookie("YYJV3NHVBPHLD36FWP6F3EM5PTXJ2XZQS7U4HWRIDPP4IWGUKUIB4XG5N26CZRDLDF7PKOXBPD6BNTUAJLETLZOIWMCVFI3K6MYZIY4QBIXIMXYDJNUKFGJVQTN5356SAD6WPCIHWNQAG7DDMF7L7S3SHBUOPCIUXDX4MQEAYEPUFFOAD4WJECT4R3K22T24MKC7OMIRDLX7S55243TDVXLO25PP4UYSPTTPMNRUFXDNP4WPE566Q6V4AH32F7HT")
api.SetStoreCookie("YYJV3NHVBPHLD36FWP6F3EM5PTXJ2XZQS7U4HWRIDPP4IWGUKUIB4XG5N26CZRDLDF7PKOXBPD6BNTUAJLETLZOIWMCVFI3K6MYZIY4QBIXIMXYDJNUKFGJVQTN5356SAD6WPCIHWNQAG7DDMF7L7S3SHDYZP7PPVMRGO4VWG2JRBMKFTOGIWZ5L2XHXC3SXQ4OLX7EL4RKUPZQT6GOH63KE3EVK37L5LG7TGSDGXFQP4377YK72UB5YZG6IJH6PY25YLLCJYPMDSHKPGYBUFJ4MMMKGN6MWB37CP7XVDBBZJ3U462ENTEXH744AWCQCIG2AAE2PKYVHC")
}
func TestTest(t *testing.T) {

View File

@@ -2,34 +2,15 @@ package jdapi
import (
"errors"
"strings"
"git.rosy.net.cn/baseapi"
"git.rosy.net.cn/baseapi/utils"
)
const (
OrderStatusAddComment = "12001"
OrderStatusModifyComment = "12006"
OrderStatusTipChanged = "12008"
OrderStatusPurchased = "41000" // 也即待处理JD的消息很怪新订单消息发过来是32000但如果不是自动接单的去查却是41000接单后才变为32000
OrderStatusNew = "32000"
OrderStatusAdjust = "33080"
OrderStatusWaitOutStore = "32001"
OrderStatusFinishedPickup = "2"
OrderStatusDelivering = "33040"
OrderStatusDelivered = "33060"
OrderStatusFinished = "90000"
OrderStatusCanceled = "20020"
OrderStatusUserApplyCancel = "20030" // 这个其实不是一个状态,是一个动作
OrderStatusLocked = "20010"
OrderStatusUnlocked = "20050"
OrderStatusInfoChanged = "1"
OrderStatusPayFinishedSettle = "330901" // 订单支付完成应结
OrderStatusAdjustSettle = "330902" // 订单调整后应结
OrderStatusSwitch2SelfSettle = "330903" // 订单众包配送转自送后应结
AfsPicPrefix = "http://img10.360buyimg.com/o2o"
)
const (
@@ -71,20 +52,139 @@ const (
QueryOrderRetryCount = 1 // 因为京东到家当前不存在的订单也返回-4暂时不重试
)
var (
ErrCanNotFindOrder = errors.New("can not find order")
const (
AfsApproveTypeRefund = 1 // 退款
AfsApproveTypeReturnGoods = 2 // 退货
AfsApproveTypeRefused = 3 // 驳回
)
var (
orderOperationResultParser = genNoPageResultParser("code", "msg", "detail", "0")
const (
AfsReasonTypeGoodsQuality = 201 // 商品质量问题/做工粗糙/有瑕疵
AfsReasonTypeWrongGoods = 202 // 发错货
AfsReasonTypeMissingGoods = 203 // 部分商品未收到
AfsReasonTypeNoGoods = 501 // 全部商品未收到
AfsReasonTypeDamagedGoods = 208 // 外表损伤(压坏,磕坏等)
AfsReasonTypeGoodsQuantity = 207 // 缺斤少两
AfsReasonTypeAgreedByMerchant = 209 // 与商家协商一致
AfsReasonTypeGoodsSizeNoSame = 302 // 大小尺寸与商品描述不符
AfsReasonTypeGoodsColorNoSame = 303 // 颜色/款式/图案与描述不符
AfsReasonWrongPurchase = 402 // 误购
AfsReasonNotReceivedIntime = 502 // 未在时效内送达
)
const (
AfsDealTypeRefund = "10" // 仅退款
AfsDealTypeDirectCompensate = "30" // 直赔
AfsDealTypeReturnGoodsRefund = "40" // 退货退款
)
type AfsSkuDiscount struct {
DetailDiscountType int `json:"detailDiscountType"`
DiscountType int `json:"discountType"`
PlatPayMoney int64 `json:"platPayMoney"`
VenderPayMoney int64 `json:"venderPayMoney"`
}
type AfsServiceDetail struct {
SkuIDIsv string `json:"skuIdIsv"`
WareID int64 `json:"wareId"`
WareName string `json:"wareName"`
WareNum int `json:"wareNum"`
SkuCount int `json:"skuCount"`
PromotionType int `json:"promotionType"`
Weight float64 `json:"weight"`
AfsMoney int64 `json:"afsMoney"`
PlatPayMoney int64 `json:"platPayMoney"`
AfsSkuDiscountList []*AfsSkuDiscount `json:"afsSkuDiscountList"`
}
type AfsServiceResponse struct {
AfsServiceID int64 `json:"afsServiceId"`
AfsServiceOrder string `json:"afsServiceOrder"`
AfsServiceState int `json:"afsServiceState"`
CreateTime *utils.JavaDate `json:"createTime"`
UpdateTime *utils.JavaDate `json:"updateTime"`
CustomerMobilePhone string `json:"customerMobilePhone"`
CustomerName string `json:"customerName"`
OrderID string `json:"orderId"`
OrderSource int `json:"orderSource"`
OrderStatus int `json:"orderStatus"`
OrderType int `json:"orderType"`
PickwareAddress string `json:"pickwareAddress"`
OrderFreightMoney int64 `json:"orderFreightMoney"`
AfsFreight int64 `json:"afsFreight"`
PackagingMoney int64 `json:"packagingMoney"`
TongchengFreightMoney int64 `json:"tongchengFreightMoney"`
MealBoxMoney int64 `json:"mealBoxMoney"`
QuestionDesc string `json:"questionDesc"`
QuestionPic string `json:"questionPic"`
QuestionTypeCid int `json:"questionTypeCid"`
ApplyDeal string `json:"applyDeal"`
DeliveryState string `json:"deliveryState"`
DeliveryMan string `json:"deliveryMan"`
DeliveryMobile string `json:"deliveryMobile"`
StationID string `json:"stationId"`
StationName string `json:"stationName"`
StationNumOutSystem string `json:"stationNumOutSystem"`
VenderID string `json:"venderId"`
AfsDetailList []*AfsServiceDetail `json:"afsDetailList"`
}
type OAOSAdjustDTO struct {
SkuID int64 `json:"skuId,omitempty"`
OutSkuID string `json:"outSkuId,omitempty"`
SkuCount int `json:"skuCount"`
}
type VenderAfsSkuDTO struct {
SkuID int64 `json:"skuId"`
SkuCount int `json:"skuCount"`
PromotionType int `json:"promotionType"`
}
type OrderSettlementInfo struct {
AllAfterSaleFreight int64 `json:"allAfterSaleFreight"`
AllAfterSalePackageMoney int64 `json:"allAfterSalePackageMoney"`
BillTime *utils.JavaDate `json:"billTime"`
DistanceFreightMoney int64 `json:"distanceFreightMoney"`
FreightCommission int64 `json:"freightCommission"`
GoodsCommission int64 `json:"goodsCommission"`
GuaranteedCommission int64 `json:"guaranteedCommission"`
OrderCashOnDeliveryMoney int64 `json:"orderCashOnDeliveryMoney"`
OrderGiftCardMoney int64 `json:"orderGiftCardMoney"`
OrderID int64 `json:"orderId"`
OrgCode string `json:"orgCode"`
PackageCommission int64 `json:"packageCommission"`
PackageMoney int64 `json:"packageMoney"`
PlatDeliveryFreight int64 `json:"platDeliveryFreight"`
PlatFreightDiscountMoney int64 `json:"platFreightDiscountMoney"`
PlatOrderGoodsDiscountMoney int64 `json:"platOrderGoodsDiscountMoney"`
PlatSkuGoodsDiscountMoney int64 `json:"platSkuGoodsDiscountMoney"`
SettlementAmount int64 `json:"settlementAmount"`
StationNo string `json:"stationNo"`
UserActualPaidGoodsMoney int64 `json:"userActualPaidGoodsMoney"`
VenderDeliveryFreight int64 `json:"venderDeliveryFreight"`
VenderFreightDiscountMoney int64 `json:"venderFreightDiscountMoney"`
VenderOrderGoodsDiscountMoney int64 `json:"venderOrderGoodsDiscountMoney"`
VenderPaidTips int64 `json:"venderPaidTips"`
VenderSkuGoodsDiscountMoney int64 `json:"venderSkuGoodsDiscountMoney"`
}
var (
ErrCanNotFindOrder = errors.New("can not find order")
)
var (
orderOperationResultParser = genNoPageResultParser("code", "detail", "detail", "0")
)
// 订单列表查询接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=169&apiid=ba3027848c3c4fda9674966e2a466482
func (a *API) OrderQuery(jdParams map[string]interface{}) (retVal []interface{}, totalCount int, err error) {
@@ -226,6 +326,14 @@ func (a *API) OrderShoudSettlementService(orderId string) (map[string]interface{
return result.(map[string]interface{}), nil
}
func (a *API) OrderShoudSettlementService2(orderId string) (orderSettlement *OrderSettlementInfo, err error) {
result, err := a.OrderShoudSettlementService(orderId)
if err == nil {
err = utils.Map2StructByJson(result, &orderSettlement, false)
}
return orderSettlement, err
}
// 查询售后单详情接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=170&apiid=6805ed690b7b4776b058067312c57d98
func (a *API) GetAfsService(orderId string) (map[string]interface{}, error) {
@@ -239,23 +347,35 @@ func (a *API) GetAfsService(orderId string) (map[string]interface{}, error) {
return result.(map[string]interface{}), nil
}
func (a *API) GetAfsService2(afsOrderID string) (afsOrderInfo *AfsServiceResponse, err error) {
result, err := a.GetAfsService(afsOrderID)
if err == nil {
if err = utils.Map2StructByJson(result, &afsOrderInfo, false); err != nil {
baseapi.SugarLogger.Warnf("GetAfsService2, result:%s failed with error:%v", utils.Format4Output(result, true), err)
}
}
return afsOrderInfo, err
}
// 商家审核用户取消申请接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=169&apiid=906b430307764a3ca3698c05c72f33d0
func (a *API) OrderCancelOperate(orderId string, isAgreed bool, operator, remark string) (err error) {
jdParams := map[string]interface{}{
"orderId": orderId,
"isAgreed": isAgreed,
"operator": operator,
"operator": utils.GetAPIOperator(operator),
"remark": remark,
}
_, err = a.AccessAPINoPage("ocs/orderCancelOperate", jdParams, nil, nil, nullResultParser)
return err
}
// 订单调整接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=169&apiid=a7378109fd7243eea9efbb6231a7401c
func (a *API) AdjustOrder(orderId, operPin, remark string, oaosAdjustDTOList []*OAOSAdjustDTO) (err error) {
jdParams := map[string]interface{}{
"orderId": orderId,
"operPin": operPin,
"operPin": utils.GetAPIOperator(operPin),
"remark": remark,
"oaosAdjustDTOList": oaosAdjustDTOList,
}
@@ -269,7 +389,7 @@ func (a *API) ReceiveFailedAudit(orderId string, isAgreed bool, operator, remark
jdParams := map[string]interface{}{
"orderId": orderId,
"isAgreed": isAgreed,
"operator": operator,
"operator": utils.GetAPIOperator(operator),
"remark": remark,
}
_, err = a.AccessAPINoPage("order/receiveFailedAudit", jdParams, nil, nil, nullResultParser)
@@ -281,7 +401,7 @@ func (a *API) ReceiveFailedAudit(orderId string, isAgreed bool, operator, remark
func (a *API) UrgeDispatching(orderId, updatePin string) (err error) {
jdParams := map[string]interface{}{
"orderId": orderId,
"updatePin": updatePin,
"updatePin": utils.GetAPIOperator(updatePin),
}
_, err = a.AccessAPINoPage("bm/urgeDispatching", jdParams, nil, nil, nullResultParser)
return err
@@ -297,3 +417,71 @@ func (a *API) ConfirmReceiveGoods(orderId string) (err error) {
_, err = a.AccessAPINoPage("order/confirmReceiveGoods", jdParams, nil, nil, nullResultParser)
return err
}
// 申请售后单审核接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=170&apiid=1690f6efc0144d59823b236e0d8506a1
func (a *API) AfsOpenApprove(afsOrderID string, afsApproveType int, rejectReason string, optPin string) (err error) {
jdParams := map[string]interface{}{
"serviceOrder": afsOrderID,
"approveType": afsApproveType,
"optPin": utils.GetAPIOperator(optPin),
}
if rejectReason != "" {
jdParams["rejectReason"] = rejectReason
}
_, err = a.AccessAPINoPage("afs/afsOpenApprove", jdParams, nil, nil, nullResultParser)
return err
}
// 售后单确认收货接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=170&apiid=4826086e81934405980ae26f80d956e0
func (a *API) ConfirmReceipt(afsOrderID, pin string) (err error) {
jdParams := map[string]interface{}{
"serviceOrder": afsOrderID,
"pin": utils.GetAPIOperator(pin),
}
_, err = a.AccessAPINoPage("afs/confirmReceipt", jdParams, nil, nil, nullResultParser)
return err
}
// 商家自主发起售后接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=170&apiid=b8d1ddacb03846a8a2e78c79723c752f
func (a *API) AfsSubmit(OrderID, pin, questionTypeCode, questionDesc, questionPic, customerName, customerMobilePhone, address string, skuList []*VenderAfsSkuDTO) (afsOrderID string, err error) {
jdParams := map[string]interface{}{
"orderId": OrderID,
"pin": utils.GetAPIOperator(pin),
"questionTypeCode": questionTypeCode,
}
if questionDesc != "" {
jdParams["questionDesc"] = questionDesc
}
if questionPic != "" {
jdParams["questionPic"] = questionPic
}
if customerName != "" {
jdParams["customerName"] = customerName
}
if customerMobilePhone != "" {
jdParams["customerMobilePhone"] = customerMobilePhone
}
if address != "" {
jdParams["address"] = address
}
result, err := a.AccessAPINoPage("afs/submit", jdParams, nil, nil, genNoPageResultParser("code", "msg", "result", "0"))
if err == nil {
afsOrderID = utils.Interface2String(result.(map[string]interface{})["serviceOrder"])
}
return afsOrderID, err
}
func ProcessQuestionPic(questionPic string) (outQuestionPic string) {
if questionPic != "" {
picList := strings.Split(questionPic, ",")
picList2 := make([]string, len(picList))
for index, pic := range picList {
picList2[index] = AfsPicPrefix + "/" + pic
}
outQuestionPic = strings.Join(picList2, ",")
}
return outQuestionPic
}

View File

@@ -50,7 +50,7 @@ func TestOrderQuery(t *testing.T) {
func TestGetOrderCallbackMsg(t *testing.T) {
callbackStr := "timestamp=2018-06-27+12%3A43%3A51&sign=84096ADFAB81E224D6231269AD1F6AAB&v=1.0&jd_param_json=%7B%22billId%22%3A%22815338526000221%22%2C%22statusId%22%3A%2232000%22%2C%22timestamp%22%3A%222018-06-27+12%3A42%3A34%22%7D&token=91633f2a-c5f5-4982-a925-a220d19095c3&app_key=1dba76d40cac446ca500c0391a0b6c9d&format=json"
result, resp := api.GetOrderCallbackMsg([]byte(callbackStr))
result, resp := api.GetOrderCallbackMsg(utils.BuildRequest(http.MethodPost, "", callbackStr, ""))
if resp != nil {
t.Fatal(resp)
@@ -60,7 +60,7 @@ func TestGetOrderCallbackMsg(t *testing.T) {
func TestGetOrderDeliveryCallbackMsg(t *testing.T) {
callbackStr := "timestamp=2018-06-27%2B09%253A08%253A41&sign=54D9A8515BB5650DC3B81366E3286570&v=1.0&jd_param_json=%257B%2522createPin%2522%253A%2522JD_21134dac1c251d2%2522%252C%2522deliveryCarrierName%2522%253A%2522%25E8%25BE%25BE%25E8%25BE%25BE%25E4%25B8%2593%25E9%2580%2581%2522%252C%2522deliveryCarrierNo%2522%253A%25229966%2522%252C%2522deliveryStatus%2522%253A%252210%2522%252C%2522deliveryStatusTime%2522%253A%25222018-06-27%2B09%253A08%253A41%2522%252C%2522inputTime%2522%253A%25222018-06-27%2B09%253A08%253A41%2522%252C%2522orderId%2522%253A%2522815324888000121%2522%257D&token=91633f2a-c5f5-4982-a925-a220d19095c3&app_key=1dba76d40cac446ca500c0391a0b6c9d&format=json"
result, resp := api.GetOrderDeliveryCallbackMsg([]byte(callbackStr))
result, resp := api.GetOrderDeliveryCallbackMsg(utils.BuildRequest(http.MethodPost, "", callbackStr, ""))
if resp != nil {
t.Fatal(resp)
@@ -70,7 +70,7 @@ func TestGetOrderDeliveryCallbackMsg(t *testing.T) {
func TestGetOrderApplyCancelCallbackMsg(t *testing.T) {
callbackStr := "timestamp=2018-06-27%2B13%253A35%253A29&sign=F1398D658514D8864FFB56F5EF2C4792&v=1.0&jd_param_json=%257B%2522billId%2522%253A%2522815339944000322%2522%252C%2522remark%2522%253A%2522%25E5%2595%2586%25E5%25AE%25B6%25E7%25BC%25BA%25E8%25B4%25A7%2522%252C%2522statusId%2522%253A%252220030%2522%252C%2522timestamp%2522%253A%25222018-06-27%2B13%253A35%253A24%2522%257D&token=91633f2a-c5f5-4982-a925-a220d19095c3&app_key=1dba76d40cac446ca500c0391a0b6c9d&format=json"
result, resp := api.GetOrderApplyCancelCallbackMsg([]byte(callbackStr))
result, resp := api.GetOrderApplyCancelCallbackMsg(utils.BuildRequest(http.MethodPost, "", callbackStr, ""))
if resp != nil {
t.Fatal(resp)
@@ -81,7 +81,7 @@ func TestGetOrderApplyCancelCallbackMsg(t *testing.T) {
func TestSimulateSendNewOrderMsg(t *testing.T) {
orderInfo := &CallbackOrderMsg{
BillID: "813344594000041",
StatusID: OrderStatusNew,
StatusID: OrderStatusPurchased,
Timestamp: utils.GetCurTimeStr(),
}
params := make(url.Values)
@@ -163,7 +163,7 @@ func TestOrderAcceptOperate(t *testing.T) {
}
func TestOrderJDZBDelivery(t *testing.T) {
result, err := api.OrderJDZBDelivery("813344594000041", "")
result, err := api.OrderJDZBDelivery("911350836000622", "")
if err != nil {
t.Fatal(err.Error())
}
@@ -171,7 +171,7 @@ func TestOrderJDZBDelivery(t *testing.T) {
}
func TestModifySellerDelivery(t *testing.T) {
result, err := api.ModifySellerDelivery("813344594000041", "")
result, err := api.ModifySellerDelivery("914376668000422", "")
if err != nil {
t.Fatal(err.Error())
}
@@ -193,3 +193,27 @@ func TestDeliveryEndOrder(t *testing.T) {
}
sugarLogger.Debug(result)
}
func TestGetAfsService(t *testing.T) {
result, err := api.GetAfsService("22565438")
if err != nil {
t.Fatal(err.Error())
}
sugarLogger.Debug(utils.Format4Output(result, false))
}
func TestGetAfsService2(t *testing.T) {
result, err := api.GetAfsService2("22565438")
if err != nil {
t.Fatal(err.Error())
}
sugarLogger.Debug(utils.Format4Output(result, false))
}
func TestOrderShoudSettlementService2(t *testing.T) {
result, err := api.OrderShoudSettlementService2("914508761000241")
if err != nil {
t.Fatal(err.Error())
}
sugarLogger.Debug(utils.Format4Output(result, false))
}

View File

@@ -0,0 +1,108 @@
package jdapi
import (
"time"
"git.rosy.net.cn/baseapi/utils"
)
const (
AuditPromotionStateNew = 101 // 待开始
AuditPromotionStateOnGoing = 102 // 进行中
AuditPromotionStateEnded = 103 // 结束
AuditPromotionStateCanceled = 104 // 取消
AuditPromotionStateAll = 105 // 全部
)
type OpenPlatActivityQResponse struct {
AddLadderList []struct {
BenefitMaxCount int `json:"benefitMaxCount"`
DiscountAmount int `json:"discountAmount"`
DiscountRate float32 `json:"discountRate"`
LowMoney int `json:"lowMoney"`
LowerLimitCount int `json:"lowerLimitCount"`
OrderLadder string `json:"orderLadder"`
} `json:"addLadderList"`
Awords string `json:"awords"`
BeginDate *utils.JavaDate `json:"beginDate"`
CostRadios int `json:"costRadios"`
EndDate *utils.JavaDate `json:"endDate"`
ID int `json:"id"`
IsMerchant int `json:"isMerchant"`
OrderLadder string `json:"orderLadder"`
OrgCode string `json:"orgCode"`
OutActivityID string `json:"outActivityId"`
PromotionName string `json:"promotionName"`
SkuBeanList []struct {
SkuID int64 `json:"skuId"`
SkuName int64 `json:"skuName"`
} `json:"skuBeanList"`
State int `json:"state"`
StationBeanList []struct {
OrgCode interface{} `json:"orgCode"`
OrgName string `json:"orgName"`
OutStationNo string `json:"outStationNo"`
StationName string `json:"stationName"`
StationNo int `json:"stationNo"`
} `json:"stationBeanList"`
}
type PromotionLspQuerySkuResult struct {
BeginTime *utils.JavaDate `json:"beginTime"`
EndTime *utils.JavaDate `json:"endTime"`
LimitDaily int `json:"limitDaily"`
LimitDevice int `json:"limitDevice"`
LimitPin int `json:"limitPin"`
PlatformRatio int `json:"platformRatio"`
PromotionPrice int `json:"promotionPrice"`
PromotionState int `json:"promotionState"`
PromotionType int `json:"promotionType"`
SkuID int64 `json:"skuId"`
Source string `json:"source"`
StationNo int64 `json:"stationNo"`
StoreRatio int `json:"storeRatio"`
}
type PromotionLspQueryInfoResult struct {
BeginTime *utils.JavaDate `json:"beginTime"`
EndTime *utils.JavaDate `json:"endTime"`
PromotionInfoID int64 `json:"promotionInfoId"`
PromotionState int `json:"promotionState"`
PromotionType int `json:"promotionType"`
SkuResultList []*PromotionLspQuerySkuResult `json:"skuResultList"`
Source string `json:"source"`
}
// 此接口逐渐会被[新版订单级促销]中相应接口替换
// 根据到家活动ID查询订单级活动明细接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=196&apiid=ff1ade31ac1b4a50be760854c777b567
func (a *API) OrderDiscountQueryActivityInfoById(activityID int64, promotionType, state int, operator string) (response *OpenPlatActivityQResponse, err error) {
params := map[string]interface{}{
"activityId": activityID,
"type": promotionType,
"traceId": utils.GetUUID(),
"operator": operator,
"requestTime": time.Now().UnixNano() / 1000000,
}
if state > 0 {
params["state"] = state
}
result, err := a.AccessAPINoPage("orderdiscount/queryActivityInfoById", params, nil, nil, genNoPageResultParser("errorCode", "errorInfos", "data", "10000"))
if err == nil {
err = utils.Map2StructByJson(result, &response, false)
}
return response, err
}
// 根据到家活动ID查询单品级促销活动接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=196&apiid=a47520a9757f4b0dbac1e6d36fd1103d
func (a *API) QueryPromotionInfo(promotionInfoId int64) (promotionInfo *PromotionLspQueryInfoResult, err error) {
jdParams := map[string]interface{}{
"promotionInfoId": promotionInfoId,
}
result, err := a.AccessAPINoPage("singlePromote/queryPromotionInfo", jdParams, nil, nil, genNoPageResultParser("errorCode", "errorInfos", "data", "0"))
if err == nil {
err = utils.Map2StructByJson(result, &promotionInfo, false)
}
return promotionInfo, err
}

View File

@@ -0,0 +1,23 @@
package jdapi
import (
"testing"
"git.rosy.net.cn/baseapi/utils"
)
func TestOrderDiscountQueryActivityInfoById(t *testing.T) {
result, err := api.OrderDiscountQueryActivityInfoById(1297945, OrderDiscountActivityTypeManJian, 0, "test")
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}
func TestQueryPromotionInfo(t *testing.T) {
result, err := api.QueryPromotionInfo(43430316)
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}

View File

@@ -1,11 +1,11 @@
package jdapi
import (
"time"
"git.rosy.net.cn/baseapi/utils"
)
// 新版订单级促销
const (
OrderDiscountActivityTypeManJian = 1 // 满减
OrderDiscountActivityTypeHuanGou = 1202 // 换购
@@ -32,7 +32,7 @@ type OrderDiscountRuleRequest struct {
DiscountRate float32 `json:"discountRate,omitempty"`
AddPrice int `json:"addPrice,omitempty"` // 分
GiftList []*OrderDiscountGift `json:"giftList,omitempty"`
LadderLimit int `json:"ladderLimit,omitempty"`
LadderLimit int `json:"ladderLimit"`
}
type OrderDiscountActivity struct {
@@ -44,11 +44,11 @@ type OrderDiscountActivity struct {
ParticipationMode int `json:"participationMode"` // 必须
OutStationNos []string `json:"outStationNos,omitempty"`
StationNos []string `json:"stationNos,omitempty"`
OutSkuIds []string `json:"outSkuIds,omitempty"`
SkuIds []string `json:"skuIds,omitempty"`
LimitOrderTotalNumber int `json:"limitOrderTotalNumber"` // 必须
LimitUserTotalNumber int `json:"limitUserTotalNumber,omitempty"`
Display string `json:"display"` // 必须
OutSkuIDs []string `json:"outSkuIds,omitempty"`
SkuIDs []string `json:"skuIds,omitempty"`
LimitOrderTotalNumber int `json:"limitOrderTotalNumber"` // 必须, 活动限购单量(0为不限不超过10万)
LimitUserTotalNumber int `json:"limitUserTotalNumber,omitempty"` // 用户限购单量(0为不限不超过100仅满减和每满减有效)
Display string `json:"display"` // 必须
RuleRequestList []*OrderDiscountRuleRequest `json:"ruleRequestList,omitempty"`
}
@@ -69,19 +69,45 @@ type ActivityOpQueryResultResponse struct {
}
type ActivityOpQueryInfoResponse struct {
ActivityID int64 `json:"activityID"`
OutActivityID string `json:"outActivityId"`
PromotionName string `json:"promotionName"`
BeginDate time.Time `json:"beginDate"`
EndDate time.Time `json:"endDate"`
Awords string `json:"awords"`
State int `json:"state"`
OrderLadder string `json:"orderLadder"`
StationList []int `json:"stationList"`
SkuList []int `json:"skuList"`
LadderList []int `json:"ladderList"`
ActivityID int64 `json:"activityID"`
OutActivityID string `json:"outActivityId"`
PromotionName string `json:"promotionName"`
BeginDate *utils.JavaDate `json:"beginDate"`
EndDate *utils.JavaDate `json:"endDate"`
Awords string `json:"awords"`
State int `json:"state"`
OrderLadder string `json:"orderLadder"`
StationList []struct {
OrgName string `json:"orgName"`
StationNo int64 `json:"stationNo"`
OutStationNo string `json:"outStationNo"`
StationName string `json:"stationName"`
} `json:"stationList"`
SkuList []struct {
OrgName string `json:"orgName"`
SkuID int64 `json:"skuId"`
OutSkuID string `json:"outSkuId"`
SkuName string `json:"skuName"`
} `json:"skuList"`
LadderList []struct {
BenefitMaxCount int `json:"benefitMaxCount"`
DiscountAmount int `json:"discountAmount"`
DiscountRate float32 `json:"discountRate"`
LowMoney int `json:"lowMoney"`
LowerLimitCount int `json:"lowerLimitCount"`
OpGiftSkuList []struct {
OrgName string `json:"orgName"`
SkuID int64 `json:"skuId"`
OutSkuID string `json:"outSkuId"`
SkuName string `json:"skuName"`
StockCount int `json:"stockCount"`
} `json:"opGiftSkuList"`
OrderLadder string `json:"orderLadder"`
} `json:"ladderList"`
}
// 订单级促销活动提交接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=211&apiid=cf98af94d4124ca287af6cfe48f0f3aa
func (a *API) OrderDiscountSubmitActivity(actInfo *OrderDiscountActivity) (activityID int64, err error) {
result, err := a.AccessAPINoPage("orderdiscount/submitActivity", utils.Struct2FlatMap(actInfo), nil, nil, nil)
if err == nil {
@@ -90,60 +116,46 @@ func (a *API) OrderDiscountSubmitActivity(actInfo *OrderDiscountActivity) (activ
return 0, err
}
// 订单级促销活动查询活动提交处理结果接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=211&apiid=b04e5426948349a78db1c5c9585d8df7
func (a *API) OrderDiscountQuerySubmitActivityResult(activityID int64) (response *ActivityOpQueryResultResponse, err error) {
result, err := a.AccessAPINoPage("orderdiscount/querySubmitActivityResult", map[string]interface{}{
"activityId": activityID,
"operator": "jxc4",
"operator": utils.APIPin,
"traceId": utils.GetUUID(),
}, nil, nil, nil)
}, nil, nil, genNoPageResultParser("code", "detail", "result", "0"))
if err == nil {
resultMap := result.(map[string]interface{})
response = &ActivityOpQueryResultResponse{
SubCode: utils.Interface2String(resultMap["subCode"]),
SubMsg: utils.Interface2String(resultMap["subMsg"]),
}
for _, v := range resultMap["resultList"].([]interface{}) {
vMap := v.(map[string]interface{})
response.ResultList = append(response.ResultList, &ActivityOpResultInfo{
ActivityID: utils.MustInterface2Int64(vMap["activityId"]),
PromName: utils.Interface2String(vMap["promName"]),
StationNo: utils.MustInterface2Int64(vMap["stationNo"]),
SkuID: utils.MustInterface2Int64(vMap["skuId"]),
OutActivityID: utils.Interface2String(vMap["outActivityId"]),
OutSkuID: utils.Interface2String(vMap["outSkuId"]),
OutStationNo: utils.Interface2String(vMap["outStationNo"]),
})
}
err = utils.Map2StructByJson(result.([]interface{})[0], &response, true) // todo只取第一个
}
return response, err
}
// func (a *API) OrderDiscountQueryActivityInfo(activityID int64) (response *OrderDiscountResultResponse, err error) {
// result, err := a.AccessAPINoPage("orderdiscount/queryActivityInfo", map[string]interface{}{
// "activityId": activityID,
// "sourceFrom": 3,
// "operator": "",
// "traceId": "",
// "version": "",
// }, nil, nil, nil)
// if err == nil {
// resultMap := result.(map[string]interface{})
// response = &OrderDiscountResultResponse{
// SubCode: utils.Interface2String(resultMap["subCode"]),
// SubMsg: utils.Interface2String(resultMap["subMsg"]),
// }
// for _, v := range resultMap["resultList"].([]interface{}) {
// vMap := v.(map[string]interface{})
// response.ResultList = append(response.ResultList, &OrderDiscountResultInfo{
// ActivityID: utils.MustInterface2Int64(vMap["activityId"]),
// PromName: utils.Interface2String(vMap["promName"]),
// StationNo: utils.MustInterface2Int64(vMap["stationNo"]),
// SkuID: utils.MustInterface2Int64(vMap["skuId"]),
// OutActivityId: utils.Interface2String(vMap["outActivityId"]),
// OutSkuId: utils.Interface2String(vMap["outSkuId"]),
// OutStationNo: utils.Interface2String(vMap["outStationNo"]),
// })
// }
// }
// return response, err
// }
// 订单级促销活动查询促销详情接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=211&apiid=116d7dfe0b4e4122be300ce26b4e5381
func (a *API) OrderDiscountQueryActivityInfo(activityID int64) (response *ActivityOpQueryInfoResponse, err error) {
result, err := a.AccessAPINoPage("orderdiscount/queryActivityInfo", map[string]interface{}{
"activityId": activityID,
"sourceFrom": 3,
"operator": utils.APIPin,
"traceId": "",
"version": "",
}, nil, nil, genNoPageResultParser("code", "detail", "result", "0"))
if err == nil {
err = utils.Map2StructByJson(result, &response, true)
}
return response, err
}
// 订单级促销活动取消接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=211&apiid=42509fdfeec14105a516b07b774a3055
func (a *API) OrderDiscountCancelActivity(activityID int64, operator, traceID string) (err error) {
if traceID == "" {
traceID = utils.GetUUID()
}
_, err = a.AccessAPINoPage("orderdiscount/cancelActivity", map[string]interface{}{
"activityId": activityID,
"operator": utils.GetAPIOperator(operator),
"traceId": traceID,
}, nil, nil, nil)
return err
}

View File

@@ -9,21 +9,22 @@ import (
func TestOrderDiscountSubmitActivity(t *testing.T) {
actInfo := &OrderDiscountActivity{
OutActivityID: utils.GetUUID(),
PromName: "测试0404",
PromType: OrderDiscountActivityTypeManJian,
BeginTime: utils.Time2Str(time.Now().Add(1 * time.Hour)),
EndTime: utils.Time2Str(time.Now().Add(48 * time.Hour)),
ParticipationMode: ParticipationModeAllSku,
Display: "hello",
OutStationNos: []string{"25"},
OutSkuIds: []string{"5199"},
OutActivityID: utils.GetUUID(),
PromName: "测试0404",
PromType: OrderDiscountActivityTypeManJian,
BeginTime: utils.Time2Str(time.Now().Add(1 * time.Hour)),
EndTime: utils.Time2Str(time.Now().Add(48 * time.Hour)),
ParticipationMode: ParticipationModePartSku,
Display: "hello",
OutStationNos: []string{"2"},
// OutSkuIDs: []string{"5199"},
SkuIDs: []string{"2023747677"},
LimitUserTotalNumber: 1,
RuleRequestList: []*OrderDiscountRuleRequest{
&OrderDiscountRuleRequest{
LowerLimitAmount: 100,
DiscountAmount: 1000,
DiscountRate: 0.5,
LowerLimitAmount: 1000,
DiscountAmount: 100,
DiscountRate: 8.5,
},
},
}
@@ -35,9 +36,24 @@ func TestOrderDiscountSubmitActivity(t *testing.T) {
}
func TestOrderDiscountQuerySubmitActivityResult(t *testing.T) {
result, err := api.OrderDiscountQuerySubmitActivityResult(3)
result, err := api.OrderDiscountQuerySubmitActivityResult(10000051)
if err != nil {
t.Fatal(err)
}
t.Log(result)
}
func TestOrderDiscountQueryActivityInfo(t *testing.T) {
result, err := api.OrderDiscountQueryActivityInfo(10000051)
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}
func TestOrderDiscountCancelActivity(t *testing.T) {
err := api.OrderDiscountCancelActivity(10000049, "test", "")
if err != nil {
t.Fatal(err)
}
}

View File

@@ -29,30 +29,24 @@ const (
PromotionStateEnded = 9
)
type PromotionSkuResult struct {
LimitDaily int
LimitDevice int
LimitPin int
PlatformRatio int
PromotionPrice int
SkuId int64
StationNo int64
StoreRatio int
}
type PromotionSku struct {
SkuID int64 `json:"skuId,omitempty"`
OutSkuID string `json:"outSkuId,omitempty"`
StationNo int64 `json:"stationNo,omitempty"`
OutStationNo string `json:"outStationNo,omitempty"`
type PromotionInfo struct {
BeginTime time.Time
EndTime time.Time
PromotionInfoId int64
PromotionState int
PromotionType int
Source string
SkuResultList []*PromotionSkuResult
PromotionPrice int64 `json:"promotionPrice,omitempty"`
LimitSkuCount int `json:"limitSkuCount,omitempty"`
FailReason string `json:"failReason,omitempty"`
}
func getPromotionCmd(inCmd string, promotionType int) (outCmd string) {
if promotionType == PromotionTypeDirectDown {
outCmd = "singlePromote/" + inCmd
if inCmd == "adjustPromotionSku" || inCmd == "adjustPromotionTime" {
outCmd = "promotesku/" + inCmd
} else {
outCmd = "singlePromote/" + inCmd
}
} else if promotionType == PromotionTypeLimitedTime {
outCmd = "limitTime/" + inCmd
} else {
@@ -63,86 +57,115 @@ func getPromotionCmd(inCmd string, promotionType int) (outCmd string) {
// 单品直降添加主活动信息接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=178&apiid=ee8685c9be9b4aa5bdc41468c5ebc33b
func (a *API) CreatePromotionInfosSingle(name string, beginDate, endDate time.Time, outInfoId, advertising string) (infoId int64, err error) {
return a.createPromotionInfos(PromotionTypeDirectDown, name, beginDate, endDate, outInfoId, advertising)
func (a *API) CreatePromotionInfosSingle(name string, beginDate, endDate time.Time, outInfoId, advertising, traceId string) (infoId int64, err error) {
return a.createPromotionInfos(PromotionTypeDirectDown, name, beginDate, endDate, outInfoId, advertising, traceId)
}
// 单品直降添加活动规则信息接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=178&apiid=900a9577cb304bbbb59abb7c2c33854d
func (a *API) CreatePromotionRulesSingle(infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int, traceId string) (err error) {
return a.createPromotionRules(PromotionTypeDirectDown, infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily, traceId)
}
// 单品直降添加活动商品信息接口
// 最多200条
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=178&apiid=0ad0715e0aaa42489cbeac36398e916d
func (a *API) CreatePromotionSkuSingle(infoId int64, outInfoId string, skus []map[string]interface{}) (skusResult []map[string]interface{}, err error) {
return a.createPromotionSku(PromotionTypeDirectDown, infoId, outInfoId, skus)
func (a *API) CreatePromotionSkuSingle(infoId int64, outInfoId string, skus []*PromotionSku, traceId string) (skusResult []*PromotionSku, err error) {
return a.createPromotionSku(PromotionTypeDirectDown, infoId, outInfoId, skus, traceId)
}
// 单品直降活动提交保存接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=178&apiid=b84e14eb341e470db8ed9b60f78edb16
func (a *API) ConfirmPromotionSingle(infoId int64, outInfoId string) (err error) {
return a.confirmPromotion(PromotionTypeDirectDown, infoId, outInfoId)
func (a *API) ConfirmPromotionSingle(infoId int64, outInfoId, traceId string) (err error) {
return a.confirmPromotion(PromotionTypeDirectDown, infoId, outInfoId, traceId)
}
// 单品直降活动部分商品取消接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=178&apiid=cfd14470b3fe4625bd2405b20bf8cec4
func (a *API) CancelPromotionSkuSingle(infoId int64, outInfoId string, cancelSkus []*PromotionSku, traceId string) (err error) {
return a.cancelPromotionSku(PromotionTypeDirectDown, infoId, outInfoId, cancelSkus, traceId)
}
// 单品直降整个活动取消接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=178&apiid=3d02c2acad714bea86d20d498a1aa074
func (a *API) CancelPromotionSingle(infoId int64, outInfoId string) (err error) {
return a.cancelPromotion(PromotionTypeDirectDown, infoId, outInfoId)
func (a *API) CancelPromotionSingle(infoId int64, outInfoId, traceId string) (err error) {
return a.cancelPromotion(PromotionTypeDirectDown, infoId, outInfoId, traceId)
}
// 单品实时促销活动结束时间调整接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=178&apiid=0abab0c4b81d45e5bc555cc7dfbeb1ad
func (a *API) AdjustPromotionTimeSingle(infoId int64, outInfoId string, endDate time.Time, traceId string) (err error) {
return a.adjustPromotionTime(PromotionTypeDirectDown, infoId, outInfoId, endDate, traceId)
}
// 单品实时促销商品促销数量调整接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=178&apiid=82964e5e0f9c448db072a54ed20e00c4
func (a *API) AdjustPromotionSkuSingle(infoId int64, outInfoId string, skus []*PromotionSku, traceId string) (skusResult []*PromotionSku, err error) {
return a.adjustPromotionSku(PromotionTypeDirectDown, infoId, outInfoId, skus, traceId)
}
// 以下为限时抢
// 限时抢添加活动主信息接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=184&apiid=7d8b7ff86c9e457bb8a46963cb575769
func (a *API) CreatePromotionInfosLimitTime(name string, beginDate, endDate time.Time, outInfoId, advertising string) (infoId int64, err error) {
return a.createPromotionInfos(PromotionTypeLimitedTime, name, beginDate, endDate, outInfoId, advertising)
func (a *API) CreatePromotionInfosLimitTime(name string, beginDate, endDate time.Time, outInfoId, advertising, traceId string) (infoId int64, err error) {
return a.createPromotionInfos(PromotionTypeLimitedTime, name, beginDate, endDate, outInfoId, advertising, traceId)
}
// 限时抢添加活动规则信息接口(直降也是调用此接口)
// 限时抢添加活动规则信息接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=184&apiid=e80674791d3542f0a87502753c0d0592
func (a *API) CreatePromotionRules(infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int) (err error) {
jdParams := map[string]interface{}{
"limitDevice": limitDevice,
"limitPin": limitPin,
"limitCount": limitCount,
"limitDaily": limitDaily,
"timeStamp": utils.GetCurTimeStr(),
}
if infoId != 0 {
jdParams[KeyInfoId] = infoId
} else {
jdParams[KeyOutInfoId] = outInfoId
}
_, err = a.AccessAPINoPage("limitTime/createPromotionRules", jdParams, nil, nil, genNoPageResultParser("errorCode", "errorInfos", "", "0"))
return err
func (a *API) CreatePromotionRulesLimitTime(infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int, traceId string) (err error) {
return a.createPromotionRules(PromotionTypeLimitedTime, infoId, outInfoId, limitDevice, limitPin, limitCount, limitDaily, traceId)
}
// 限时抢添加活动商品信息接口
// 最多200条
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=184&apiid=65fecef4883c40c6b23bbdb6123f5d80
func (a *API) CreatePromotionSkuLimitTime(infoId int64, outInfoId string, skus []map[string]interface{}) (skusResult []map[string]interface{}, err error) {
return a.createPromotionSku(PromotionTypeLimitedTime, infoId, outInfoId, skus)
func (a *API) CreatePromotionSkuLimitTime(infoId int64, outInfoId string, skus []*PromotionSku, traceId string) (skusResult []*PromotionSku, err error) {
return a.createPromotionSku(PromotionTypeLimitedTime, infoId, outInfoId, skus, traceId)
}
// 限时抢活动提交保存接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=184&apiid=3991063e2f0d435094e9fe44754f3490
func (a *API) ConfirmPromotionLimitTime(infoId int64, outInfoId string) (err error) {
return a.confirmPromotion(PromotionTypeLimitedTime, infoId, outInfoId)
func (a *API) ConfirmPromotionLimitTime(infoId int64, outInfoId, traceId string) (err error) {
return a.confirmPromotion(PromotionTypeLimitedTime, infoId, outInfoId, traceId)
}
// 限时抢活动取消部分商品接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=184&apiid=01bd37df882749af91be4b040873ccc6
func (a *API) CancelPromotionSkuLimitTime(infoId int64, outInfoId string, cancelSkus []*PromotionSku, traceId string) (err error) {
return a.cancelPromotionSku(PromotionTypeLimitedTime, infoId, outInfoId, cancelSkus, traceId)
}
// 限时抢整个活动取消接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=184&apiid=97b6678d30624f73bc13cb68987e6a6d
func (a *API) CancelPromotionLimitTime(infoId int64, outInfoId string) (err error) {
return a.cancelPromotion(PromotionTypeLimitedTime, infoId, outInfoId)
func (a *API) CancelPromotionLimitTime(infoId int64, outInfoId, traceId string) (err error) {
return a.cancelPromotion(PromotionTypeLimitedTime, infoId, outInfoId, traceId)
}
func (a *API) createPromotionInfos(promotionType int, name string, beginDate, endDate time.Time, outInfoId, advertising string) (infoId int64, err error) {
// 限时抢活动结束时间调整接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=184&apiid=f91035295cd54a9bbd2db9dfc800484c
func (a *API) AdjustPromotionTimeLimitTime(infoId int64, outInfoId string, endDate time.Time, traceId string) (err error) {
return a.adjustPromotionTime(PromotionTypeLimitedTime, infoId, outInfoId, endDate, traceId)
}
// 限时抢商品促销数量调整接口
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=184&apiid=aa878a000dfb4a248634ee755af1f1d3
func (a *API) AdjustPromotionSkuLimitTime(infoId int64, outInfoId string, skus []*PromotionSku, traceId string) (skusResult []*PromotionSku, err error) {
return a.adjustPromotionSku(PromotionTypeLimitedTime, infoId, outInfoId, skus, traceId)
}
func (a *API) createPromotionInfos(promotionType int, name string, beginDate, endDate time.Time, outInfoId, advertising, traceId string) (infoId int64, err error) {
if outInfoId == "" {
outInfoId = fmt.Sprintf("%X", md5.Sum([]byte(name)))
}
jdParams := map[string]interface{}{
"promotionName": name,
"beginDate": utils.Time2Str(beginDate),
"endDate": utils.Time2Str(endDate),
"advertising": advertising,
"promotionType": promotionType,
"timeStamp": utils.GetCurTimeStr(),
KeyOutInfoId: outInfoId,
}
jdParams := getCommonSkuPromotionParams(0, outInfoId, traceId)
jdParams["promotionName"] = name
jdParams["beginDate"] = utils.Time2Str(beginDate)
jdParams["endDate"] = utils.Time2Str(endDate)
jdParams["advertising"] = advertising
jdParams["promotionType"] = promotionType
jdParams["advertising"] = advertising
result, err := a.AccessAPINoPage(getPromotionCmd("createPromotionInfos", promotionType), jdParams, nil, nil, genNoPageResultParser("errorCode", "errorInfos", "data", "0"))
if err == nil {
return utils.MustInterface2Int64(result), nil
@@ -150,35 +173,64 @@ func (a *API) createPromotionInfos(promotionType int, name string, beginDate, en
return 0, err
}
// todo skusResult 返回值没有意义,为了兼容暂时保留
func (a *API) createPromotionSku(promotionType int, infoId int64, outInfoId string, skus []map[string]interface{}) (skusResult []map[string]interface{}, err error) {
jdParams := map[string]interface{}{
"skus": skus,
"timeStamp": utils.GetCurTimeStr(),
}
if infoId != 0 {
jdParams[KeyInfoId] = infoId
} else {
jdParams[KeyOutInfoId] = outInfoId
}
_, err = a.AccessAPINoPage(getPromotionCmd("createPromotionSku", promotionType), jdParams, nil, nil, genNoPageResultParser("errorCode", "data", "", "0"))
return nil, err
func (a *API) createPromotionRules(promotionType int, infoId int64, outInfoId string, limitDevice, limitPin, limitCount, limitDaily int, traceId string) (err error) {
jdParams := getCommonSkuPromotionParams(infoId, outInfoId, traceId)
jdParams["limitDevice"] = limitDevice
jdParams["limitPin"] = limitPin
jdParams["limitCount"] = limitCount
jdParams["limitDaily"] = limitDaily
_, err = a.AccessAPINoPage(getPromotionCmd("createPromotionRules", promotionType), jdParams, nil, nil, genNoPageResultParser("errorCode", "errorInfos", "", "0"))
return err
}
func (a *API) confirmPromotion(promotionType int, infoId int64, outInfoId string) (err error) {
jdParams := map[string]interface{}{
"timeStamp": utils.GetCurTimeStr(),
}
if infoId != 0 {
jdParams[KeyInfoId] = infoId
} else {
jdParams[KeyOutInfoId] = outInfoId
// todo skusResult 返回值没有意义,为了兼容暂时保留
func (a *API) createPromotionSku(promotionType int, infoId int64, outInfoId string, skus []*PromotionSku, traceId string) (skusResult []*PromotionSku, err error) {
jdParams := getCommonSkuPromotionParams(infoId, outInfoId, traceId)
jdParams["skus"] = skus
// todo 当前是在出错时把data中的数据当成错误信息处理的
result, err := a.AccessAPINoPage(getPromotionCmd("createPromotionSku", promotionType), jdParams, nil, nil, genNoPageResultParser("errorCode", "data", "", "0"))
if err == nil && result != nil {
err = utils.Map2StructByJson(result, &skusResult, false)
}
return skusResult, err
}
func (a *API) confirmPromotion(promotionType int, infoId int64, outInfoId, traceId string) (err error) {
jdParams := getCommonSkuPromotionParams(infoId, outInfoId, traceId)
_, err = a.AccessAPINoPage(getPromotionCmd("confirmPromotion", promotionType), jdParams, nil, nil, genNoPageResultParser("errorCode", "errorInfos", "", "0"))
return err
}
func (a *API) cancelPromotion(promotionType int, infoId int64, outInfoId string) (err error) {
func (a *API) cancelPromotionSku(promotionType int, infoId int64, outInfoId string, cancelSkus []*PromotionSku, traceId string) (err error) {
jdParams := getCommonSkuPromotionParams(infoId, outInfoId, traceId)
jdParams["cancelSkus"] = cancelSkus
_, err = a.AccessAPINoPage(getPromotionCmd("cancelPromotionSku", promotionType), jdParams, nil, nil, genNoPageResultParser("errorCode", "errorInfos", "", "0"))
return err
}
func (a *API) cancelPromotion(promotionType int, infoId int64, outInfoId, traceId string) (err error) {
jdParams := getCommonSkuPromotionParams(infoId, outInfoId, traceId)
_, err = a.AccessAPINoPage(getPromotionCmd("cancelPromotion", promotionType), jdParams, nil, nil, genNoPageResultParser("errorCode", "errorInfos", "", "0"))
return err
}
func (a *API) adjustPromotionTime(promotionType int, infoId int64, outInfoId string, endDate time.Time, traceId string) (err error) {
jdParams := getCommonSkuPromotionParams(infoId, outInfoId, traceId)
_, err = a.AccessAPINoPage(getPromotionCmd("adjustPromotionTime", promotionType), jdParams, nil, nil, genNoPageResultParser("errorCode", "errorInfos", "", "0"))
return err
}
func (a *API) adjustPromotionSku(promotionType int, infoId int64, outInfoId string, skus []*PromotionSku, traceId string) (skusResult []*PromotionSku, err error) {
jdParams := getCommonSkuPromotionParams(infoId, outInfoId, traceId)
jdParams["skus"] = skus
result, err := a.AccessAPINoPage(getPromotionCmd("adjustPromotionSku", promotionType), jdParams, nil, nil, genNoPageResultParser("errorCode", "data", "", "0"))
if err == nil && result != nil {
err = utils.Map2StructByJson(result, &skusResult, false)
}
return skusResult, err
}
func getCommonSkuPromotionParams(infoId int64, outInfoId, traceId string) map[string]interface{} {
jdParams := map[string]interface{}{
"timeStamp": utils.GetCurTimeStr(),
}
@@ -187,42 +239,8 @@ func (a *API) cancelPromotion(promotionType int, infoId int64, outInfoId string)
} else {
jdParams[KeyOutInfoId] = outInfoId
}
_, err = a.AccessAPINoPage(getPromotionCmd("cancelPromotion", promotionType), jdParams, nil, nil, genNoPageResultParser("errorCode", "errorInfos", "", "0"))
return err
}
func (a *API) QueryPromotionInfo(promotionInfoId int64) (promotionInfo *PromotionInfo, err error) {
jdParams := map[string]interface{}{
"promotionInfoId": promotionInfoId,
if traceId != "" {
jdParams["traceId"] = traceId
}
result, err := a.AccessAPINoPage("singlePromote/queryPromotionInfo", jdParams, nil, nil, genNoPageResultParser("errorCode", "errorInfos", "data", "0"))
if err == nil {
data := result.(map[string]interface{})
// baseapi.SugarLogger.Debug(utils.Format4Output(data, false))
promotionInfo = &PromotionInfo{
BeginTime: utils.Timestamp2Time(utils.MustInterface2Int64(data["beginTime"].(map[string]interface{})["time"]) / 1000),
EndTime: utils.Timestamp2Time(utils.MustInterface2Int64(data["endTime"].(map[string]interface{})["time"]) / 1000),
PromotionInfoId: utils.MustInterface2Int64(data["promotionInfoId"]),
PromotionState: int(utils.MustInterface2Int64(data["promotionState"])),
PromotionType: int(utils.MustInterface2Int64(data["promotionType"])),
Source: utils.Interface2String(data["source"]),
}
skuResultList := data["skuResultList"].([]interface{})
promotionInfo.SkuResultList = make([]*PromotionSkuResult, len(skuResultList))
for k, v := range skuResultList {
skuResult := v.(map[string]interface{})
promotionInfo.SkuResultList[k] = &PromotionSkuResult{
LimitDaily: int(utils.MustInterface2Int64(skuResult["limitDaily"])),
LimitDevice: int(utils.MustInterface2Int64(skuResult["limitDevice"])),
LimitPin: int(utils.MustInterface2Int64(skuResult["limitPin"])),
PlatformRatio: int(utils.MustInterface2Int64(skuResult["platformRatio"])),
PromotionPrice: int(utils.MustInterface2Int64(skuResult["promotionPrice"])),
SkuId: utils.MustInterface2Int64(skuResult["skuId"]),
StationNo: utils.MustInterface2Int64(skuResult["stationNo"]),
StoreRatio: int(utils.MustInterface2Int64(skuResult["storeRatio"])),
}
}
return promotionInfo, nil
}
return nil, err
return jdParams
}

View File

@@ -6,67 +6,57 @@ import (
)
func TestCreatePromotionSingle(t *testing.T) {
infoId, err := api.CreatePromotionInfosSingle("测试1", time.Now(), time.Now().Add(24*time.Hour), "", "")
infoId, err := api.CreatePromotionInfosSingle("测试1", time.Now(), time.Now().Add(24*time.Hour), "", "", "")
if err != nil {
t.Fatal(err)
}
t.Log(infoId)
err = api.CreatePromotionRules(infoId, "", 1, 1, 1, 1)
err = api.CreatePromotionRulesSingle(infoId, "", 1, 1, 1, 1, "")
if err != nil {
t.Fatal(err)
}
skuInfos, err := api.CreatePromotionSkuSingle(infoId, "", []map[string]interface{}{
{
KeyOutSkuId: "2216",
KeyStationNo: 11682042,
KeyPromotionPrice: 500,
KeyLimitSkuCount: 2,
skuInfos, err := api.CreatePromotionSkuSingle(infoId, "", []*PromotionSku{
&PromotionSku{
OutSkuID: "2216",
StationNo: 11682042,
PromotionPrice: 500,
LimitSkuCount: 2,
},
})
}, "")
if err != nil {
t.Fatal(err)
}
t.Log(skuInfos)
err = api.ConfirmPromotionSingle(infoId, "")
err = api.ConfirmPromotionSingle(infoId, "", "")
if err != nil {
t.Fatal(err)
}
}
func TestCreatePromotionLimitTime(t *testing.T) {
infoId, err := api.CreatePromotionInfosLimitTime("测试1", time.Now(), time.Now().Add(24*time.Hour), "", "")
infoId, err := api.CreatePromotionInfosLimitTime("测试1", time.Now(), time.Now().Add(24*time.Hour), "", "", "")
if err != nil {
t.Fatal(err)
}
t.Log(infoId)
err = api.CreatePromotionRules(infoId, "", 1, 1, 5, 1)
err = api.CreatePromotionRulesLimitTime(infoId, "", 1, 1, 5, 1, "")
if err != nil {
t.Fatal(err)
}
skuInfos, err := api.CreatePromotionSkuLimitTime(infoId, "", []map[string]interface{}{
{
KeyOutSkuId: "2216",
KeyStationNo: 11682042,
KeyPromotionPrice: 300,
KeyLimitSkuCount: 2,
skuInfos, err := api.CreatePromotionSkuLimitTime(infoId, "", []*PromotionSku{
&PromotionSku{
OutSkuID: "2216",
StationNo: 11682042,
PromotionPrice: 300,
LimitSkuCount: 2,
},
})
}, "")
if err != nil {
t.Fatal(err)
}
t.Log(skuInfos)
err = api.ConfirmPromotionLimitTime(infoId, "")
err = api.ConfirmPromotionLimitTime(infoId, "", "")
if err != nil {
t.Fatal(err)
}
}
func TestQueryPromotionInfo(t *testing.T) {
result, err := api.QueryPromotionInfo(14885272)
if err != nil {
t.Fatal(err)
}
if len(result.SkuResultList) == 0 {
t.Fatal("should have SkuResultList")
}
}

View File

@@ -80,7 +80,7 @@ const (
)
const (
MaxSkuNameLen = 45 // skuname最长字符数
MaxSkuNameCharCount = 45 // skuname最长字符数
SaleAttrIDBase = 1001
SaleAttrValueIDBase = 10
)
@@ -399,13 +399,7 @@ func (a *API) QueryKeyWordDicInfo(pageNo, pageSize int, keyValue string) (values
// 商家商品状态同步接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=180&apiid=5e29d6c9317847e58b8cbcc70702fd52
func (a *API) SyncProduct(storeId, skuId string) (retVal bool, err error) {
result, err := a.AccessAPINoPage("search/syncProduct", utils.Params2Map(KeyStoreId, storeId, KeySkuId, skuId), nil, nil, func(data map[string]interface{}) (interface{}, error) {
status := utils.MustInterface2Int64(data["status"])
if status == 200 {
return data["synchronized"].(bool), nil
}
return nil, utils.NewErrorIntCode(data["message"].(string), int(status))
})
result, err := a.AccessAPINoPage("search/syncProduct", utils.Params2Map(KeyStoreId, storeId, KeySkuId, skuId), nil, nil, genNoPageResultParser("status", "message", "synchronized", "200"))
if err == nil {
return result.(bool), nil
}

View File

@@ -58,12 +58,12 @@ func TestBatchUpdateOutSkuId(t *testing.T) {
func TestQuerySkuInfos(t *testing.T) {
pageSize := 20
result, totalCount, err := api.QuerySkuInfos("", 0, 0, pageSize, true)
result, totalCount, err := api.QuerySkuInfos("一个高级商品", 0, 0, pageSize, true)
if err != nil {
t.Fatal(err)
}
if len(result) != pageSize || totalCount == 0 {
baseapi.SugarLogger.Debug(result)
baseapi.SugarLogger.Debug(utils.Format4Output(result, false))
t.Fatalf("QuerySkuInfos result size is not same as requested:%d", pageSize)
}
}
@@ -95,32 +95,32 @@ func TestQueryKeyWordDicInfo(t *testing.T) {
}
func TestSyncProduct(t *testing.T) {
result, err := api.SyncProduct("11732425", "2015717812")
result, err := api.SyncProduct(mustExistStoreID, "2022250244")
if err != nil {
t.Fatal(err)
}
baseapi.SugarLogger.Debug(result)
result, err = api.SyncProduct("wrongstoreid", "2015717812")
result, err = api.SyncProduct("wrongstoreid", "2022250244")
if err == nil {
t.Fatal("SyncProduct should return error")
}
result, err = api.SyncProduct("11732425", "wrongskuid")
result, err = api.SyncProduct(mustExistStoreID, "wrongskuid")
if err == nil {
t.Fatal("SyncProduct should return error")
}
}
func TestGetProductStatust(t *testing.T) {
result, err := api.GetProductStatus("11732425", "2015717812")
result, err := api.GetProductStatus(mustExistStoreID, "2022250244")
if err != nil || result == nil {
t.Fatal(err)
}
// baseapi.SugarLogger.Debug(result)
result, err = api.GetProductStatus("wrongstoreid", "2015717812")
result, err = api.GetProductStatus("wrongstoreid", "2022250244")
if err == nil {
t.Fatal("GetProductStatus should return error")
}
result, err = api.GetProductStatus("11732425", "wrongskuid")
result, err = api.GetProductStatus(mustExistStoreID, "wrongskuid")
if err == nil {
t.Fatal("GetProductStatus should return error")
}

View File

@@ -35,6 +35,23 @@ type CreateShopResult struct {
StationNo string `json:"stationNo"`
}
type CityInfo struct {
AreaCode int `json:"areaCode"`
AreaLevel int `json:"areaLevel"`
AreaName string `json:"areaName"`
ParentAreaID int `json:"parentAreaId"`
TencentAddressCode int `json:"tencentAddressCode"`
Yn int `json:"yn"`
}
func (a *API) GetAllCities() (cities []*CityInfo, err error) {
result, err := a.AccessAPINoPage("address/allcities", nil, nil, nil, genNoPageResultParser("code", "msg", "result", "0"))
if err == nil {
err = utils.Map2StructByJson(result, &cities, false)
}
return cities, err
}
// 获取门店编码列表接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=194&apiid=138426aa19b54c48ae8464af1ca3b681
func (a *API) GetStationsByVenderId() ([]string, error) {
@@ -125,7 +142,7 @@ func (a *API) OrgReplyComment(orderID int64, storeID, content, replayPin string)
"orderId": orderID,
"storeId": storeID,
"content": content,
"replyPin": replayPin,
"replyPin": utils.GetAPIOperator(replayPin),
}
_, err := a.AccessAPINoPage("commentOutApi/orgReplyComment", jdParams, nil, nil, genNoPageResultParser("code", "msg", "result", "200"))
return err

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"net/http"
"net/url"
"strings"
"git.rosy.net.cn/baseapi"
"git.rosy.net.cn/baseapi/platformapi"
@@ -11,8 +12,9 @@ import (
)
const (
accessStorePageCookieName = "shop.o2o.jd.com1"
getStoreURL = "https://daojia.jd.com"
getStoreURL = "https://daojia.jd.com"
accessStorePageCookieName = "shop.o2o.jd.com1"
accessStorePageCookieName2 = "lsp-store1.jddj.com"
)
type SkuPageImg struct {
@@ -21,6 +23,48 @@ type SkuPageImg struct {
Small string `json:"small"`
}
type CorporationInfo struct {
Scope string `json:"scope"`
OperName string `json:"oper_name"`
Status string `json:"status"`
BelongOrg string `json:"belong_org"`
CreditNo string `json:"credit_no"`
RegNo string `json:"reg_no"`
ID string `json:"id"`
OrgNo string `json:"org_no"`
EconKind string `json:"econ_kind"`
EndDate string `json:"end_date"`
TermEnd string `json:"term_end"`
NeedID bool `json:"needID"`
Address string `json:"address"`
Partners []struct {
IdentifyType string `json:"identify_type"`
ShouldCapiItems []interface{} `json:"should_capi_items"`
StockType string `json:"stock_type"`
IdentifyNo string `json:"identify_no"`
RealCapiItems []interface{} `json:"real_capi_items"`
Name string `json:"name"`
} `json:"partners"`
Name string `json:"name"`
Province string `json:"province"`
TermStart string `json:"term_start"`
AbnormalItems []interface{} `json:"abnormal_items"`
CheckDate string `json:"check_date"`
RegistCapi string `json:"regist_capi"`
StartDate string `json:"start_date"`
Changerecords []struct {
BeforeContent string `json:"before_content"`
ChangeDate string `json:"change_date"`
ChangeItem string `json:"change_item"`
AfterContent string `json:"after_content"`
} `json:"changerecords"`
Branches []interface{} `json:"branches"`
Employees []struct {
JobTitle string `json:"job_title"`
Name string `json:"name"`
} `json:"employees"`
}
func (a *API) SetStoreCookie(storeCookie string) {
a.locker.Lock()
defer a.locker.Unlock()
@@ -33,14 +77,21 @@ func (a *API) GetStoreCookie() string {
return a.storeCookie
}
func (a *API) AccessStorePage(fullURL string) (retVal map[string]interface{}, err error) {
func (a *API) AccessStorePage(fullURL string, formData map[string]interface{}) (retVal map[string]interface{}, err error) {
storeCookie := a.GetStoreCookie()
if storeCookie == "" {
return nil, fmt.Errorf("需要设置Store Cookie才能使用此方法")
}
err = platformapi.AccessPlatformAPIWithRetry(a.client,
func() *http.Request {
request, _ := http.NewRequest(http.MethodGet, fullURL, nil)
var request *http.Request
if formData == nil {
request, _ = http.NewRequest(http.MethodGet, fullURL, nil)
} else {
request, _ = http.NewRequest(http.MethodPost, fullURL, strings.NewReader(utils.Map2URLValues(formData).Encode()))
request.Header.Set("charset", "UTF-8")
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
if err != nil {
return nil
}
@@ -48,10 +99,14 @@ func (a *API) AccessStorePage(fullURL string) (retVal map[string]interface{}, er
Name: accessStorePageCookieName,
Value: storeCookie,
})
request.AddCookie(&http.Cookie{
Name: accessStorePageCookieName2,
Value: storeCookie,
})
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (errLevel string, err error) {
func(response *http.Response, jsonResult1 map[string]interface{}) (errLevel string, err error) {
retVal = jsonResult1
code := jsonResult1["code"].(string)
if code == ResponseCodeSuccess {
@@ -84,7 +139,7 @@ func (a *API) GetStoreOrderInfo(orderId, stationNo string) (storeOrderInfo map[s
if stationNo != "" {
urlStr += "&stationNo=" + stationNo
}
retVal, err := a.AccessStorePage(urlStr)
retVal, err := a.AccessStorePage(urlStr, nil)
// baseapi.SugarLogger.Debug(utils.Format4Output(retVal, false))
if err == nil {
newOrderinfoMains := retVal["newOrderinfoMains"].(map[string]interface{})
@@ -102,7 +157,7 @@ func (a *API) GetStoreOrderInfoList(fromTime, toTime string) (storeOrderList []m
pageNo := 1
urlTemplate := "http://store.jddj.com/order/newManager/tabQuery/all?o2oOrderType=10000&pageNo=%d&pageSize=%d&orderBy=&desc=true&startTimeQuery=%s&endTimeQuery=%s&stationNo="
for {
retVal, err := a.AccessStorePage(fmt.Sprintf(urlTemplate, pageNo, pageSize, url.QueryEscape(fromTime), url.QueryEscape(toTime)))
retVal, err := a.AccessStorePage(fmt.Sprintf(urlTemplate, pageNo, pageSize, url.QueryEscape(fromTime), url.QueryEscape(toTime)), nil)
// baseapi.SugarLogger.Debug(utils.Format4Output(retVal, false))
if err == nil {
newOrderinfoMains := retVal["newOrderinfoMains"].(map[string]interface{})
@@ -124,10 +179,7 @@ func (a *API) GetSkuPageInfo(skuId int64) (skuPageInfo map[string]interface{}, e
"skuId": utils.Int64ToStr(skuId),
"storeId": "0",
}
skuPageInfo, err = a.AccessStorePage(fmt.Sprintf("https://daojia.jd.com/client?platCode=H5&functionId=product/detailV6_0&body=%s", utils.Format4Output(skuIDMap, true)))
if err == nil {
baseapi.SugarLogger.Debug(utils.Format4Output(skuPageInfo, false))
}
skuPageInfo, err = a.AccessStorePage(fmt.Sprintf("https://daojia.jd.com/client?platCode=H5&functionId=product/detailV6_0&body=%s", utils.Format4Output(skuIDMap, true)), nil)
return skuPageInfo, err
}
@@ -140,34 +192,22 @@ func (a *API) GetSkuPageImageInfo(skuId int64) (imgList []*SkuPageImg, err error
}
func (a *API) GetStoreInfo(storeId string) (storeInfo map[string]interface{}, err error) {
retVal, err := a.AccessStorePage2(fmt.Sprintf("client?functionId=store/storeDetailV220&body={\"storeId\":\"%s\"}&appVersion=6.1.0", storeId))
return retVal, err
}
func (a *API) AccessStorePage2(subURL string) (retVal map[string]interface{}, err error) {
err = platformapi.AccessPlatformAPIWithRetry(a.client,
func() *http.Request {
fullURL := utils.GenerateGetURL(getStoreURL, subURL, nil)
request, _ := http.NewRequest(http.MethodGet, fullURL, nil)
if err != nil {
return nil
}
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (errLevel string, err error) {
code := jsonResult1["code"].(string)
if code == ResponseCodeSuccess && jsonResult1["result"] != nil {
retVal = jsonResult1["result"].(map[string]interface{})
return platformapi.ErrLevelSuccess, nil
}
newErr := utils.NewErrorCode(jsonResult1["msg"].(string), code)
return platformapi.ErrLevelCodeIsNotOK, newErr
})
retVal, err := a.AccessStorePage(fmt.Sprintf("https://daojia.jd.com/client?functionId=store/storeDetailV220&body={\"storeId\":\"%s\"}&appVersion=6.1.0", storeId), nil)
return retVal, err
}
func (a *API) GetStoreList(lng string, lat string) (retVal map[string]interface{}, err error) {
retVal, err = a.AccessStorePage2(fmt.Sprintf("client?platCode=h5&appVersion=6.5.0&functionId=zone/recommendStoreList&body={\"channelId\":\"3997\",\"currentPage\":1,\"pageSize\":999,\"coordType\":\"2\",\"platform\":\"1\"}&signKey=b63f63fa9e27123b84a0c80ef5cd210d&lng=%s&lat=%s", lng, lat))
retVal, err = a.AccessStorePage(fmt.Sprintf("https://daojia.jd.com/client?platCode=h5&appVersion=6.5.0&functionId=zone/recommendStoreList&body={\"channelId\":\"3997\",\"currentPage\":1,\"pageSize\":999,\"coordType\":\"2\",\"platform\":\"1\"}&signKey=b63f63fa9e27123b84a0c80ef5cd210d&lng=%s&lat=%s", lng, lat), nil)
return retVal, err
}
func (a *API) GetCorporationInfo(stationNo, qualifyNumber string) (corporatonInfo *CorporationInfo, err error) {
result, err := a.AccessStorePage("https://sta-store.jddj.com/store/requestQualify.o2o", map[string]interface{}{
"stationNo": stationNo,
"qualifyNumber": qualifyNumber,
})
if err == nil {
err = utils.Map2StructByJson(result, &corporatonInfo, false)
}
return corporatonInfo, err
}

View File

@@ -54,3 +54,27 @@ func TestGetSkuPageImageInfo(t *testing.T) {
}
baseapi.SugarLogger.Debug(utils.Format4Output(imgList, false))
}
func TestGetCorporationInfo(t *testing.T) {
imgList, err := api.GetCorporationInfo("", "915101003431062533")
if err != nil {
t.Fatal(err)
}
baseapi.SugarLogger.Debug(utils.Format4Output(imgList, false))
}
func TestGetStoreList(t *testing.T) {
result, err := api.GetStoreList("104.057218", "30.6949")
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}
func TestGetStoreInfo(t *testing.T) {
result, err := api.GetStoreInfo("11750116")
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}

View File

@@ -41,6 +41,38 @@ type SkuIdEntity struct {
OutSkuId string `json:"outSkuId"`
}
type StorePriceInfo struct {
MarketPrice int64 `json:"marketPrice"`
Pin string `json:"pin"`
Price int64 `json:"price"`
PromoteVipPrice int64 `json:"promoteVipPrice"`
SkuID int64 `json:"skuId"`
StationNo string `json:"stationNo"`
VenderID string `json:"venderId"`
VipPrice int64 `json:"vipPrice"`
}
type QueryStockResponse struct {
SkuID int64 `json:"skuId"`
StationNo string `json:"stationNo"`
UsableQty int `json:"usableQty"`
LockQty int `json:"lockQty"`
OrderQty int `json:"orderQty"`
Vendibility int `json:"vendibility"`
}
type UpdateVendibilityResponse struct {
Code int `json:"code"`
CurrentQty int `json:"currentQty"`
LockQty int `json:"lockQty"`
Msg string `json:"msg"`
OrderQty int `json:"orderQty"`
OutSkuID string `json:"outSkuId"`
SkuID int64 `json:"skuId"`
UsableQty int `json:"usableQty"`
Vendibility int `json:"vendibility"`
}
// 传入为数组的最多一次为50个
// 有好些功能有两个类似的函数一个为到家ID一个为商家ID建议都只用商家ID的那个因为
// 1这类函数一般可以批量操作
@@ -85,16 +117,16 @@ func (a *API) UpdateStationPrice(skuId int64, stationNo string, price int) (stri
// 根据到家商品编码和到家门店编码批量查询商品门店价格信息接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=205&apiid=21ccd5a00d3a4582b4c9a8ef0ae238fc
func (a *API) GetStationInfoList(stationNo string, skuIds []int64) ([]map[string]interface{}, error) {
func (a *API) GetStationInfoList(stationNo string, skuIds []int64) (priceInfo []*StorePriceInfo, err error) {
jdParams := map[string]interface{}{
"skuIds": skuIds,
"stationNo": stationNo,
}
result, err := a.AccessAPINoPage("price/getStationInfoList", jdParams, nil, nil, nil)
result, err := a.AccessAPINoPage("price/getStationInfoList", jdParams, nil, nil, genNoPageResultParser("code", "detail", "result", "0"))
if err == nil && result != nil {
return utils.Slice2MapSlice(result.([]interface{})), nil
err = utils.Map2StructByJson(result, &priceInfo, false)
}
return nil, err
return priceInfo, err
}
// 根据商家商品编码和商家门店编码批量修改现货库存接口
@@ -105,7 +137,7 @@ func (a *API) BatchUpdateCurrentQtys(outStationNo, stationNo string, skuStockLis
}
jdParams := map[string]interface{}{
"skuStockList": skuStockList,
"userPin": userPin,
"userPin": utils.GetAPIOperator(userPin),
}
if outStationNo != "" {
jdParams["outStationNo"] = outStationNo
@@ -157,7 +189,7 @@ func (a *API) BatchUpdateVendibility(outStationNo, stationNo string, stockVendib
}
jdParams := map[string]interface{}{
"stockVendibilityList": stockVendibilityList,
"userPin": userPin,
"userPin": utils.GetAPIOperator(userPin),
}
if outStationNo != "" {
jdParams["outStationNo"] = outStationNo
@@ -174,28 +206,28 @@ func (a *API) BatchUpdateVendibility(outStationNo, stationNo string, stockVendib
// 根据到家商品编码和门店编码批量查询商品库存及可售状态信息接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=200&apiid=bc6ad75e8fd34580856e06b5eb149aad
// 尽量不用这个接口,用下面那个
func (a *API) QueryOpenUseable(listBaseStockCenterRequest []*BaseStockCenterRequest) ([]map[string]interface{}, error) {
func (a *API) QueryOpenUseable(listBaseStockCenterRequest []*BaseStockCenterRequest) (stockResponse []*QueryStockResponse, err error) {
jdParams := map[string]interface{}{
"listBaseStockCenterRequest": listBaseStockCenterRequest,
}
result, err := a.AccessAPINoPage("stock/queryOpenUseable", jdParams, nil, nil, genNoPageResultParser("retCode", "retMsg", "data", "0"))
if err == nil && result != nil {
return utils.Slice2MapSlice(result.([]interface{})), nil
err = utils.Map2StructByJson(result, &stockResponse, false)
}
return nil, err
return stockResponse, err
}
// 根据商家商品编码和门店编码批量查询商品库存及可售状态信息接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=200&apiid=ba70316bb84f425f8c088d3c19b2570d
func (a *API) QueryStockCenter(outStationNo string, skuIds []*SkuIdEntity, userPin string) ([]map[string]interface{}, error) {
func (a *API) QueryStockCenter(outStationNo string, skuIds []*SkuIdEntity, userPin string) (vendibilityResponse []*UpdateVendibilityResponse, err error) {
jdParams := map[string]interface{}{
"outStationNo": outStationNo,
"skuIds": skuIds,
"userPin": userPin,
"userPin": utils.GetAPIOperator(userPin),
}
result, err := a.AccessAPINoPage("stock/queryStockCenter", jdParams, nil, nil, genNoPageResultParser("retCode", "retMsg", "data", "0"))
if err == nil && result != nil {
return utils.Slice2MapSlice(result.([]interface{})), nil
err = utils.Map2StructByJson(result, &vendibilityResponse, false)
}
return nil, err
return vendibilityResponse, err
}

View File

@@ -8,8 +8,8 @@ import (
)
const (
mustExistSkuID = 2017194325
mustExistSkuJXID = "21206"
mustExistSkuID = 2023747677
mustExistSkuJXID = "5246"
)
func TestGetStationInfoList(t *testing.T) {
@@ -17,9 +17,7 @@ func TestGetStationInfoList(t *testing.T) {
if err != nil {
t.Fatal(err)
}
for _, v := range result {
baseapi.SugarLogger.Debug(v)
}
t.Log(utils.Format4Output(result, false))
}
func TestQueryOpenUseable(t *testing.T) {
@@ -32,9 +30,7 @@ func TestQueryOpenUseable(t *testing.T) {
if err != nil {
t.Fatal(err)
}
for _, v := range result {
baseapi.SugarLogger.Debug(v)
}
t.Log(utils.Format4Output(result, false))
}
func TestQueryStockCenter(t *testing.T) {
@@ -46,9 +42,7 @@ func TestQueryStockCenter(t *testing.T) {
if err != nil {
t.Fatal(err)
}
for _, v := range result {
baseapi.SugarLogger.Debug(v)
}
t.Log(utils.Format4Output(result, false))
}
func TestBatchUpdateVendibility(t *testing.T) {

View File

@@ -9,10 +9,18 @@ import (
)
const (
mustExistStoreID = "11738324"
mustExistStoreJXID = "100285"
mustExistStoreID = "11053496"
mustExistStoreJXID = "2"
)
func TestGetAllCities(t *testing.T) {
result, err := api.GetAllCities()
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}
func TestGetStationsByVenderId(t *testing.T) {
result, err := api.GetStationsByVenderId()
if err != nil {

View File

@@ -215,7 +215,7 @@ func (a *API) AccessAPI(action string, params map[string]interface{}) (retVal *R
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (result string, err error) {
func(response *http.Response, jsonResult1 map[string]interface{}) (result string, err error) {
code := int(utils.MustInterface2Int64(jsonResult1["code"]))
retVal = &ResponseResult{
Code: code,

View File

@@ -42,7 +42,7 @@ const (
type FullDiscountActInfo struct {
ActIDs string `json:"act_ids"`
ActName string `json:"act_name"`
StartTime int64 `json:"start_time"` // 活动开始时间,单s位秒
StartTime int64 `json:"start_time"` // 活动开始时间,单位秒
EndTime int64 `json:"end_time"` // 活动结束时间,单位秒
ActType int `json:"act_type"`
Status int `json:"-"`
@@ -79,52 +79,105 @@ type ShippingFeeActDetail struct {
LimitPrice float64 `json:"limit_price"`
DiscountPrice float64 `json:"discount_price"`
PoiCharge float64 `json:"-"`
MtCharge float64 `json:"-"`
// list时用
PoiCharge float64 `json:"poi_charge,omitempty"`
MtCharge float64 `json:"mt_charge,omitempty"`
}
type ShippingFeeActData struct {
StartTime int64 `json:"start_time"` // 活动开始时间,单s位秒
StartTime int64 `json:"start_time"` // 活动开始时间,单位秒
EndTime int64 `json:"end_time"` // 活动结束时间,单位秒
WeeksTime string `json:"weeks_time,omitempty"`
Period string `json:"period,omitempty"`
ActDetail []*ShippingFeeActDetail `json:"act_detail"`
MaxPrice float64 `json:"max_price,omitempty"`
AppPoiCode string `json:"-"`
ActID string `json:"-"`
ActStatus string `json:"-"`
// list时用
AppPoiCode string `json:"app_poi_code,omitempty"`
ActID int64 `json:"act_id,omitempty"`
ActStatus string `json:"actStatus,omitempty"`
}
type RetailActData struct {
type RetailDiscountActData struct {
AppFoodCode string `json:"app_food_code"`
UserType int `json:"user_type"`
StartTime int64 `json:"start_time"` // 活动开始时间,单s位秒
StartTime int64 `json:"start_time"` // 活动开始时间,单位秒
EndTime int64 `json:"end_time"` // 活动结束时间,单位秒
OrderLimit int `json:"order_limit"`
DayLimit int `json:"day_limit"`
WeeksTime string `json:"weeks_time"`
Period string `json:"period"`
WeeksTime string `json:"weeks_time"`
SettingType int `json:"setting_type"`
ActPrice float64 `json:"act_price"` // 折扣价格(单位元)必须为大于0的数字且不能超过2位小数。
DiscountCoefficient float64 `json:"discount_coefficient"` // 折扣系数必须大于0小于9.8最多支持两位小数。如输入3即为3折
Sequence int `json:"sequence,omitempty"`
ItemID int64 `json:"item_id,omitempty"` // 活动ID为什么这里又是int64
// 以下参数只有查询时用
OriginalPrice float64 `json:"-"` // 商品原价,单位元
Stock int `json:"-"` // 当日剩余活动商品数量。只有当发起查询时间处于活动生效时段内时(start_time、end_time、period、weeks_time均需满足),该字段才代表实际剩余活动商品数量,否则显示的是创建活动时规定的当日活动库存
Status int `json:"-"` // 活动状态0:已过期1:已生效2:待生效。
Name string `json:"-"`
// list时用
OriginalPrice float64 `json:"origin_price,omitempty"` // 商品原价,单位元
Stock int `json:"stock,omitempty"` // 当日剩余活动商品数量。只有当发起查询时间处于活动生效时段内时(start_time、end_time、period、weeks_time均需满足),该字段才代表实际剩余活动商品数量,否则显示的是创建活动时规定的当日活动库存
Status int `json:"status,omitempty"` // 活动状态0:已过期1:已生效2:待生效。
Name string `json:"name,omitempty"`
}
type RetailActDataLimit struct {
type RetailDiscountActResult struct {
AppFoodCode string `json:"app_food_code"`
StartTime int64 `json:"start_time"` // 活动开始时间,单位秒
EndTime int64 `json:"end_time"` // 活动结束时间,单位秒
ActID int64 `json:"act_id,omitempty"`
Period string `json:"period"`
WeeksTime string `json:"weeks_time"`
}
type RetailDiscountActDataLimit struct {
ItemID int64 `json:"item_id"` // 活动ID为什么这里又是int64
DayLimit int `json:"day_limit,omitempty"` // 当日活动库存,只能为正整数或-1-1表示无限库存
OrderLimit int `json:"order_limit,omitempty"` // 每单可购买的折扣商品数量
}
func (a *API) FullDiscountBatchSave(poiCode string, actInfo *FullDiscountActInfo, actList []*FullDiscountActDetail, actSkuList []*FullDiscountSku) (actID int64, err error) {
type LimitTime struct {
StartTime int64 `json:"start_time"` // 活动开始时间,单位秒
EndTime int64 `json:"end_time"` // 活动结束时间,单位秒
}
type CouponInfo struct {
LimitPrice float64 `json:"limit_price"` //使用门槛,单位元
CouponPrice float64 `json:"coupon_price"` // 优惠券金额,单位元
UserType int `json:"user_type"` // 可领用户类型,新客、老客、全部
ValidityDays int `json:"validity_days"` //使用有效期,用户领用了此商家券后在领用日起多久内可有效使用
Stock int `json:"stock"`
}
type CouponResult struct {
StartTime int64 `json:"start_time"` // 活动开始时间,单位秒
EndTime int64 `json:"end_time"` // 活动结束时间,单位秒
ActID int64 `json:"act_id,omitempty"`
}
type CouponData struct {
LimitPrice float64 `json:"limit_price"` //使用门槛,单位元
CouponPrice float64 `json:"coupon_price"` // 优惠券金额,单位元
UserType int `json:"user_type"` // 可领用户类型,新客、老客、全部
ValidityDays int `json:"validity_days"` //使用有效期,用户领用了此商家券后在领用日起多久内可有效使用
Stock int `json:"stock"`
UseCount int `json:"use_count"` // 劵已使用数量
SentCount int `json:"sent_count"` // 劵已发数量
}
type CouponActInfo struct {
AppPoiCode string `json:"app_poi_code"`
StartTime int64 `json:"start_time"` // 活动开始时间,单位秒
EndTime int64 `json:"end_time"` // 活动结束时间,单位秒
ActData []*CouponData `json:"act_data"` // 活动数据
ActID int64 `json:"actId"`
RemainDays int `json:"remain_days"` // 活动剩余天数
Status int `json:"status"` // 活动状态
}
// 批量创建指定商品满减活动或创建店内满减活动
// http://developer.waimai.meituan.com/home/docDetail/255
func (a *API) FullDiscountBatchSave(poiCode string, actInfo *FullDiscountActInfo, actList []*FullDiscountActDetail, actSkuList []*FullDiscountSku) (actIDList []int64, err error) {
params := map[string]interface{}{
KeyAppPoiCode: poiCode,
"act_info": string(utils.MustMarshal(actInfo)),
@@ -135,11 +188,13 @@ func (a *API) FullDiscountBatchSave(poiCode string, actInfo *FullDiscountActInfo
}
result, err := a.AccessAPI2("act/full/discount/batchsave", false, params, resultKeyMsg)
if err == nil {
return utils.MustInterface2Int64(result.([]interface{})[0].(map[string]interface{})["act_id"]), nil
err = utils.UnmarshalUseNumber([]byte(result.(string)), &actIDList)
}
return 0, err
return actIDList, err
}
// 批量查询指定商品满减活动或店内满减活动
// http://developer.waimai.meituan.com/home/docDetail/256
func (a *API) FullDiscountList(poiCode string, actType int) (actInfoList []*FullDiscountActData, err error) {
result, err := a.AccessAPI("act/full/discount/list", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
@@ -151,6 +206,8 @@ func (a *API) FullDiscountList(poiCode string, actType int) (actInfoList []*Full
return actInfoList, err
}
// 批量删除指定商品满减活动或店内满减活动
// http://developer.waimai.meituan.com/home/docDetail/257
func (a *API) FullDiscountDelete(poiCode string, actIDList []string, actType int) (err error) {
_, err = a.AccessAPI("act/full/discount/delete", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
@@ -160,6 +217,8 @@ func (a *API) FullDiscountDelete(poiCode string, actIDList []string, actType int
return err
}
// 批量添加活动商品至指定商品满减活动
// http://developer.waimai.meituan.com/home/docDetail/258
func (a *API) FullDiscountFoodsBatchSave(poiCode, actID string, appFoodList []*FullDiscountSku) (err error) {
_, err = a.AccessAPI("act/full/discount/foods/batchsave", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
@@ -169,6 +228,8 @@ func (a *API) FullDiscountFoodsBatchSave(poiCode, actID string, appFoodList []*F
return err
}
// 批量查询指定商品满减活动中的活动商品
// http://developer.waimai.meituan.com/home/docDetail/259
func (a *API) FullDiscountFoodsList(poiCode, actID string) (actFoodsInfo *FullDiscountFoodsInfo, err error) {
limit := 200
for {
@@ -201,6 +262,7 @@ func (a *API) FullDiscountFoodsList(poiCode, actID string) (actFoodsInfo *FullDi
// 批量删除活动商品至指定商品满减活动
// appFoodCodeList 删除商品数量上限为100如果删除门店多个活动商品用英文逗号隔开
// http://developer.waimai.meituan.com/home/docDetail/260
func (a *API) FullDiscountFoodsDelete(poiCode, actID string, appFoodCodeList []string) (err error) {
_, err = a.AccessAPI("act/full/discount/foods/delete", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
@@ -211,6 +273,7 @@ func (a *API) FullDiscountFoodsDelete(poiCode, actID string, appFoodCodeList []s
}
// 批量修改指定商品满减活动中商品的当日活动库存
// http://developer.waimai.meituan.com/home/docDetail/261
func (a *API) FullDiscountFoodsDayLimit(poiCode, actID string, appFoodList []*FullDiscountSku) (err error) {
_, err = a.AccessAPI("act/full/discount/foods/daylimit", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
@@ -222,6 +285,7 @@ func (a *API) FullDiscountFoodsDayLimit(poiCode, actID string, appFoodList []*Fu
// 阶梯满减配送费活动一个门店只可能有一个活动所以不涉及actID的概念
// 批量创建阶梯满减配送费活动
// http://developer.waimai.meituan.com/home/docDetail/281
func (a *API) FulllDiscountShippingFeeBatchSave(poiCode string, actData []*ShippingFeeActData) (err error) {
_, err = a.AccessAPI("act/full/discount/shippingfee/batchsave", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
@@ -231,6 +295,7 @@ func (a *API) FulllDiscountShippingFeeBatchSave(poiCode string, actData []*Shipp
}
// 查询阶梯满减配送费活动
// http://developer.waimai.meituan.com/home/docDetail/282
func (a *API) FulllDiscountShippingFeeList(poiCode string) (actList []*ShippingFeeActData, err error) {
result, err := a.AccessAPI("act/full/discount/shippingfee/list", true, map[string]interface{}{
KeyAppPoiCode: poiCode,
@@ -241,20 +306,32 @@ func (a *API) FulllDiscountShippingFeeList(poiCode string) (actList []*ShippingF
return actList, err
}
// 批量删除阶梯满减配送费活动
// http://developer.waimai.meituan.com/home/docDetail/283
func (a *API) FulllDiscountShippingFeeBatchDelete(poiCode string, actIDList []string) (err error) {
_, err = a.AccessAPI("act/full/discount/shippingfee/batchdelete", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
"act_ids": strings.Join(actIDList, ","),
})
return err
}
// 批量创建或更新零售折扣商品
func (a *API) RetailDiscountBatchSave(poiCode string, actData []*RetailActData) (actID int64, err error) {
// http://developer.waimai.meituan.com/home/docDetail/287
func (a *API) RetailDiscountBatchSave(poiCode string, actData []*RetailDiscountActData) (actResult []*RetailDiscountActResult, err error) {
result, err := a.AccessAPI2("act/retail/discount/batchsave", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
"act_data": string(utils.MustMarshal(actData)),
}, resultKeyMsg)
if err == nil {
return utils.MustInterface2Int64(result.([]interface{})[0].(map[string]interface{})["act_id"]), nil
err = utils.UnmarshalUseNumber([]byte(result.(string)), &actResult)
}
return 0, err
return actResult, err
}
// 查询门店零售折扣商品
func (a *API) RetailDiscountList(poiCode string) (actList []*RetailActData, err error) {
// http://developer.waimai.meituan.com/home/docDetail/288
func (a *API) RetailDiscountList(poiCode string) (actList []*RetailDiscountActData, err error) {
limit := 200
offset := 0
for {
@@ -264,7 +341,7 @@ func (a *API) RetailDiscountList(poiCode string) (actList []*RetailActData, err
"offset": offset,
})
if err == nil {
var tmpActList []*RetailActData
var tmpActList []*RetailDiscountActData
if err = utils.Map2StructByJson(result, &tmpActList, false); err != nil {
return nil, err
}
@@ -281,6 +358,7 @@ func (a *API) RetailDiscountList(poiCode string) (actList []*RetailActData, err
}
// 批量删除零售折扣商品
// http://developer.waimai.meituan.com/home/docDetail/289
func (a *API) RetailDiscountDelete(poiCode string, actIDList []string) (err error) {
_, err = a.AccessAPI("act/retail/discount/batchdelete", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
@@ -290,7 +368,8 @@ func (a *API) RetailDiscountDelete(poiCode string, actIDList []string) (err erro
}
// 批量修改零售折扣商品当日活动库存
func (a *API) RetailDiscountBatchStock(poiCode, actDataList []*RetailActDataLimit) (err error) {
// http://developer.waimai.meituan.com/home/docDetail/290
func (a *API) RetailDiscountBatchStock(poiCode, actDataList []*RetailDiscountActDataLimit) (err error) {
_, err = a.AccessAPI("act/retail/discount/batchstock", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
"act_data": string(utils.MustMarshal(actDataList)),
@@ -299,10 +378,50 @@ func (a *API) RetailDiscountBatchStock(poiCode, actDataList []*RetailActDataLimi
}
// 批量修改零售折扣商品每单限购数量
func (a *API) RetailDiscountBatchLimit(poiCode, actDataList []*RetailActDataLimit) (err error) {
// http://developer.waimai.meituan.com/home/docDetail/291
func (a *API) RetailDiscountBatchLimit(poiCode, actDataList []*RetailDiscountActDataLimit) (err error) {
_, err = a.AccessAPI("act/retail/discount/batchlimit", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
"act_data": string(utils.MustMarshal(actDataList)),
})
return err
}
// 批量新增商家券(店内发券)活动
// http://developer.waimai.meituan.com/home/docDetail/312
func (a *API) InStoreCouponBatchSave(poiCode string, limitTime *LimitTime, couponInfoList []*CouponInfo) (couponResultList []*CouponResult, err error) {
result, err := a.AccessAPI2("act/instore/coupon/batchsave", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
"limit_time": string(utils.MustMarshal(limitTime)),
"act_data": string(utils.MustMarshal(couponInfoList)),
}, resultKeyMsg)
if err == nil {
err = utils.UnmarshalUseNumber([]byte(result.(string)), &couponResultList)
}
return couponResultList, err
}
// 批量删除商家券(店内发券)活动
// http://developer.waimai.meituan.com/home/docDetail/313
func (a *API) InStoreCouponDelete(poiCode string, actIDList []string) (err error) {
_, err = a.AccessAPI("act/instore/coupon/delete", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
"act_ids": strings.Join(actIDList, ","),
})
return err
}
// 查询商家券(店内发券)活动
// http://developer.waimai.meituan.com/home/docDetail/314
// 页码从1开始
func (a *API) InStoreCouponList(poiCode string, pageNum, pageSize int) (couponActList []*CouponActInfo, err error) {
result, err := a.AccessAPI("act/instore/coupon/list", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
"page_num": pageNum,
"page_size": pageSize,
})
if err == nil {
err = utils.Map2StructByJson(result, &couponActList, false)
}
return couponActList, err
}

View File

@@ -8,7 +8,7 @@ import (
)
func TestFullDiscountBatchSave(t *testing.T) {
result, err := api.FullDiscountBatchSave("6693359", &FullDiscountActInfo{
result, err := api.FullDiscountBatchSave(testPoiCode, &FullDiscountActInfo{
// ActIDs: "12345678",
ActName: "测试活动0402",
StartTime: time.Now().Unix(),
@@ -33,7 +33,7 @@ func TestFullDiscountBatchSave(t *testing.T) {
}
func TestFullDiscountList(t *testing.T) {
result, err := api.FullDiscountList("6737142", ActTypeStoreFullDiscount)
result, err := api.FullDiscountList(testPoiCode, ActTypeStoreFullDiscount)
if err != nil {
t.Fatal(err)
}
@@ -41,7 +41,7 @@ func TestFullDiscountList(t *testing.T) {
}
func TestFullDiscountFoodsList(t *testing.T) {
result, err := api.FullDiscountFoodsList("6737142", "497726932")
result, err := api.FullDiscountFoodsList(testPoiCode, "497726932")
if err != nil {
t.Fatal(err)
}
@@ -49,14 +49,14 @@ func TestFullDiscountFoodsList(t *testing.T) {
}
func TestFulllDiscountShippingFeeBatchSave(t *testing.T) {
err := api.FulllDiscountShippingFeeBatchSave("6737142", []*ShippingFeeActData{&ShippingFeeActData{}})
err := api.FulllDiscountShippingFeeBatchSave(testPoiCode, []*ShippingFeeActData{&ShippingFeeActData{}})
if err != nil {
t.Fatal(err)
}
}
func TestFulllDiscountShippingFeeList(t *testing.T) {
result, err := api.FulllDiscountShippingFeeList("6737142")
result, err := api.FulllDiscountShippingFeeList(testPoiCode)
if err != nil {
t.Fatal(err)
}
@@ -64,8 +64,8 @@ func TestFulllDiscountShippingFeeList(t *testing.T) {
}
func TestRetailDiscountBatchSave(t *testing.T) {
result, err := api.RetailDiscountBatchSave("6737142", []*RetailActData{&RetailActData{
AppFoodCode: "24785",
result, err := api.RetailDiscountBatchSave(testPoiCode, []*RetailDiscountActData{&RetailDiscountActData{
AppFoodCode: "5246",
UserType: UserTypeAll,
StartTime: time.Now().Unix(),
EndTime: time.Now().Add(24 * time.Hour).Unix(),
@@ -81,7 +81,7 @@ func TestRetailDiscountBatchSave(t *testing.T) {
}
func TestRetailDiscountList(t *testing.T) {
result, err := api.RetailDiscountList("6737142")
result, err := api.RetailDiscountList(testPoiCode)
if err != nil {
t.Fatal(err)
}
@@ -89,8 +89,16 @@ func TestRetailDiscountList(t *testing.T) {
}
func TestRetailDiscountDelete(t *testing.T) {
err := api.RetailDiscountDelete("6737142", []string{"329187452"})
err := api.RetailDiscountDelete(testPoiCode, []string{"329187452"})
if err != nil {
t.Fatal(err)
}
}
func TestInStoreCouponList(t *testing.T) {
result, err := api.InStoreCouponList(testPoiCode, 1, 100)
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}

View File

@@ -38,10 +38,31 @@ type CallbackResponse struct {
Data string `json:"data"`
}
// !!!特别注意Data中的数据
// 全额退款没有Food, FoodList, Money项
type CallbackRefundInfo struct {
Timestamp int64 `json:"timestamp"`
AppID string `json:"app_id"`
Sig string `json:"sig"`
OrderID int64 `json:"order_id"`
NotifyType string `json:"notify_type"`
RefundID int64 `json:"refund_id"`
Ctime int64 `json:"ctime"`
Reason string `json:"reason"`
ResType int `json:"res_type"`
IsAppeal int `json:"is_appeal"`
Pictures string `json:"pictures"`
PictureList []string `json:"pictureList"`
Food string `json:"food"`
FoodList []*RefundSkuDetail `json:"foodList"`
Money float32 `json:"money"`
}
type CallbackMsg struct {
Data url.Values
Cmd string
Cmd string
FormData url.Values
Data interface{}
}
var (
@@ -77,16 +98,29 @@ func (a *API) GetCallbackMsg(request *http.Request) (msg *CallbackMsg, callbackR
msg = &CallbackMsg{
Cmd: cmd,
}
msg.Data = make(url.Values)
msg.FormData = make(url.Values)
for k, v := range data {
// 美团外卖的用户催单信息的timestamp是毫秒其它的事件是秒统一归一化为秒
if cmd == MsgTypeUserUrgeOrder && k == "timestamp" {
intV := utils.Str2Int64(v.(string))
if intV > time.Now().Unix()*2 {
v = utils.Int64ToStr(intV / 1000)
data[k] = v
}
}
msg.Data.Set(k, v.(string))
msg.FormData.Set(k, v.(string))
}
if msg.Cmd == MsgTypeOrderRefund || msg.Cmd == MsgTypeOrderPartialRefund {
var refundData *CallbackRefundInfo
if err = utils.Map2StructByJson(data, &refundData, true); err == nil {
if refundData.Food != "" {
utils.UnmarshalUseNumber([]byte(refundData.Food), &refundData.FoodList)
}
if refundData.Pictures != "" {
utils.UnmarshalUseNumber([]byte(refundData.Pictures), &refundData.PictureList)
}
msg.Data = refundData
}
}
}
} else {
@@ -98,3 +132,8 @@ func (a *API) GetCallbackMsg(request *http.Request) (msg *CallbackMsg, callbackR
}
return msg, callbackResponse
}
func (a *API) GetRefundSkuDetailFromMsg(msg *CallbackMsg) (refundSkuDetail []*RefundSkuDetail) {
utils.UnmarshalUseNumber([]byte(msg.FormData.Get("food")), &refundSkuDetail)
return refundSkuDetail
}

View File

@@ -0,0 +1,19 @@
package mtwmapi
import (
"net/url"
"testing"
"git.rosy.net.cn/baseapi/utils"
)
func TestGetRefundSkuDetailFromMsg(t *testing.T) {
data, _ := url.ParseQuery(`
/mtwm/orderPartialRefund?timestamp=1556595759&reason=%E5%BA%97%E9%93%BA%E5%A4%AA%E5%BF%99%E4%BA%86%21%E5%AE%A2%E5%AE%98%E6%88%91%E6%80%95%E6%82%A8%E7%AD%89%E7%9A%84%E5%A4%AA%E4%B9%85&food=%5B%7B%22app_food_code%22%3A%2231458%22%2C%22box_num%22%3A0%2C%22box_price%22%3A0%2C%22count%22%3A1%2C%22food_name%22%3A%22%E9%B2%9C%E7%AB%B9%E7%AC%8B%EF%BC%88%E9%B8%A1%E5%A9%86%E7%AC%8B%EF%BC%89%5B%E6%9C%AA%E6%B3%A1%E6%B0%B4%5D%E7%BA%A6250g%2F%E4%BB%BD%22%2C%22food_price%22%3A7.97%2C%22origin_food_price%22%3A7.97%2C%22refund_price%22%3A7.97%2C%22sku_id%22%3A%2231458%22%2C%22spec%22%3A%22250g%22%7D%5D&money=7.97&is_appeal=0&pictures=&notify_type=agree&app_id=589&order_id=67413510345111009&res_type=2&sig=0cef69f37b4a0e924ac4119c3d75117b
`)
msg := &CallbackMsg{
FormData: data,
}
result := api.GetRefundSkuDetailFromMsg(msg)
t.Log(utils.Format4Output(result, false))
}

View File

@@ -0,0 +1,95 @@
package mtwmapi
import (
"math"
"strings"
"git.rosy.net.cn/baseapi/utils"
)
const (
CommentReplyStatusAll = -1 //全部
CommentReplyStatusNotReplied = 0 // 未回复
CommentReplyStatusReplied = 1 // 已回复
MaxCommentQueryPageSize = 20
)
type OrderComment struct {
AddComment string `json:"add_comment"`
AddCommentTime string `json:"add_comment_time"`
CommentContent string `json:"comment_content"`
CommentID int64 `json:"comment_id"`
CommentLables string `json:"comment_lables"`
CommentLableList []string `json:"comment_lable_list"`
CommentPictures string `json:"comment_pictures"`
CommentTime string `json:"comment_time"`
CriticFoodList string `json:"critic_food_list"`
DeliveryCommentScore int `json:"delivery_comment_score"`
FoodCommentScore int `json:"food_comment_score"`
OrderCommentScore int `json:"order_comment_score"`
PackingScore int `json:"packing_score"`
PraiseFoodList string `json:"praise_food_list"`
ReplyStatus int `json:"reply_status"`
Result string `json:"result"`
ShipTime int `json:"ship_time"`
}
// 根据门店id批量查询评价信息新版
// http://developer.waimai.meituan.com/home/docDetail/191
func (a *API) CommentQuery(poiCode string, startDateStr, endDateStr string, offset, limit int, replyStatus int) (commentList []*OrderComment, err error) {
params := map[string]interface{}{
KeyAppPoiCode: poiCode,
"start_time": startDateStr,
"end_time": endDateStr,
"pageoffset": offset,
"pagesize": limit,
}
if limit <= 0 {
limit = math.MaxInt32
}
originalOffset := offset
originalLimit := limit
for {
if limit <= 0 {
break
}
if limit > MaxCommentQueryPageSize {
limit = MaxCommentQueryPageSize
}
params["pageoffset"] = offset
params["pagesize"] = limit
result, err := a.AccessAPI("comment/query", true, params)
if err != nil {
return nil, err
}
var batchCommentList []*OrderComment
err = utils.Map2StructByJson(result, &batchCommentList, false)
if err != nil {
return nil, err
}
for _, comment := range batchCommentList {
if comment.CommentLables != "" {
comment.CommentLableList = strings.Split(comment.CommentLables, ",")
}
}
commentList = append(commentList, batchCommentList...)
if len(batchCommentList) < limit {
break
}
offset += limit
limit = originalLimit + originalOffset - offset
}
return commentList, nil
}
// 根据评价id添加商家回复
// http://developer.waimai.meituan.com/home/docDetail/194
func (a *API) CommentAddReply(poiCode string, commentID int64, reply string) (err error) {
_, err = a.AccessAPI("poi/comment/add_reply", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
"comment_id": commentID,
"reply": reply,
})
return err
}

View File

@@ -0,0 +1,15 @@
package mtwmapi
import (
"testing"
"git.rosy.net.cn/baseapi/utils"
)
func TestCommentQuery(t *testing.T) {
result, err := api.CommentQuery("6783778", "20190501", "20190515", 0, 0, CommentReplyStatusAll)
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}

View File

@@ -39,6 +39,12 @@ const (
GeneralMaxLimit = 200 // 大多数的API的批处理最大条数
)
const (
ErrCodeSysErr = 700 // 系统错误,按美团外卖技术支持的说法,可当成需重试的错误
ErrCodeAccessLimited = 711 // 接口调用过于频繁,触发流控,请降低调用频率
ErrCodeNoAppFood = 805 // 不存在此菜品
)
type API struct {
appID string
secret string
@@ -47,6 +53,13 @@ type API struct {
config *platformapi.APIConfig
}
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 {
@@ -133,11 +146,14 @@ func (a *API) AccessAPI2(cmd string, isGet bool, bizParams map[string]interface{
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (errLevel string, err error) {
if _, ok := jsonResult1["error"]; ok {
func(response *http.Response, jsonResult1 map[string]interface{}) (errLevel string, err error) {
if errObj, ok := jsonResult1["error"]; ok {
baseapi.SugarLogger.Debugf("mtwm AccessAPI failed, jsonResult1:%s", utils.Format4Output(jsonResult1, true))
errorInfo := jsonResult1["error"].(map[string]interface{})
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
}
retVal = jsonResult1[resultKey]

View File

@@ -22,6 +22,7 @@ const (
const (
WaybillStatusWait4Delivery = "0" // 配送单发往配送
WaybillStatusPending = "5" // 配送压单
WaybillStatusAccepted = "10" // 配送单已确认
WaybillStatusCourierArrived = "15" // 骑手已到店
WaybillStatusPickedup = "20" // 骑手已取餐
@@ -29,6 +30,7 @@ const (
WaybillStatusCanceled = "100" // 配送单已取消
)
// APP方URL 推送用户或客服取消订单(必接)
const (
CancelReasonAcceptTimeout = 1001 // 系统取消,超时未确认
CancelReasonPayTimeout = 1002 // 系统取消在线支付订单15分钟未支付
@@ -42,6 +44,18 @@ const (
CancelReasonOther = 1301 // 其他原因
)
const (
ResTypePending = 0 // 未处理
ResTypeMerchantRefused = 1 // 商家驳回退款请求
ResTypeMerchantAgreed = 2 // 商家同意退款
ResTypeCSRefused = 3 // 客服驳回退款请求
ResTypeCSAgreed = 4 // 客服帮商家同意退款
ResTypeTimeoutAutoAgreed = 5 // 超过3小时自动同意
ResTypeAutoAgreed = 6 // 系统自动确认
ResTypeUserCancelApply = 7 // 用户取消退款申请
ResTypeUserCancelComplain = 8 // 用户取消退款申诉
)
const (
NotifyTypeApply = "apply" // 发起全款退款
NotifyTypePartyApply = "part" // 发起部分退款
@@ -55,12 +69,234 @@ const (
UserApplyCancelWaitMinute = 30 // 用户申请退款后商家未在30分钟内大连锁商家为3小时内处理退款请求系统将自动同意退款
)
const (
RefundTypeAll = 1
RefundTypePart = 2
)
const (
ExtrasPromotionTypeTaoCanZeng = 4 // 套餐赠
ExtrasPromotionTypeManZeng = 5 // 满赠
ExtrasPromotionTypeTeJiaCai = 7 // 特价菜
ExtrasPromotionTypeZheKouCai = 17 // 折扣菜
ExtrasPromotionTypeSecondHalfPrice = 20 // 第2份半价活动
ExtrasPromotionTypeShanGouBaoPin = 56 // 闪购爆品
)
type RefundSku struct {
AppFoodCode string `json:"app_food_code"`
SkuID string `json:"sku_id,omitempty"`
Count int `json:"count"`
}
type RefundSkuDetail struct {
AppFoodCode string `json:"app_food_code"`
BoxNum float32 `json:"box_num"`
BoxPrice float64 `json:"box_price"`
Count int `json:"count"`
FoodName string `json:"food_name"`
FoodPrice float64 `json:"food_price"`
OriginFoodPrice float64 `json:"origin_food_price"`
RefundPrice float64 `json:"refund_price"`
SkuID string `json:"sku_id"`
Spec string `json:"spec"`
}
type RefundOrderDetail struct {
ApplyReason string `json:"apply_reason"`
ApplyType int `json:"apply_type"`
CTime int64 `json:"ctime"`
Money float64 `json:"money"`
OrderID int64 `json:"order_id"`
Pictures string `json:"pictures"`
PictureList []string `json:"pictureList"`
RefundType int `json:"refund_type"`
ResReason string `json:"res_reason"`
ResType int `json:"res_type"`
UTime int64 `json:"utime"`
WmAppRetailForOrderPartRefundList []*RefundSkuDetail `json:"wmAppRetailForOrderPartRefundList"`
WmOrderIDView int64 `json:"wm_order_id_view"`
}
type OrderExtraInfo struct {
ActDetailID int `json:"act_detail_id,omitempty"`
MtCharge float64 `json:"mt_charge,omitempty"`
PoiCharge float64 `json:"poi_charge,omitempty"`
ReduceFee float64 `json:"reduce_fee,omitempty"`
Remark string `json:"remark,omitempty"`
Type int `json:"type,omitempty"`
RiderFee float64 `json:"rider_fee,omitempty"`
}
type FoodInfo struct {
AppFoodCode string `json:"app_food_code"`
BoxNum int `json:"box_num"`
BoxPrice int `json:"box_price"`
CartID int `json:"cart_id"`
FoodDiscount int `json:"food_discount"`
FoodName string `json:"food_name"`
FoodProperty string `json:"food_property"`
Price float64 `json:"price"`
Quantity int `json:"quantity"`
SkuID string `json:"sku_id"`
Spec string `json:"spec"`
Unit string `json:"unit"`
}
type CanRefundFoodInfo struct {
AppFoodCode string `json:"app_food_code"`
BoxNum float64 `json:"box_num"`
BoxPrice float64 `json:"box_price"`
Count int `json:"count"`
FoodName string `json:"food_name"`
FoodPrice float64 `json:"food_price"`
IsRefundDifference int `json:"is_refund_difference"`
OriginFoodPrice float64 `json:"origin_food_price"`
RefundPrice float64 `json:"refund_price"`
SkuID string `json:"sku_id"`
}
type WmAppOrderActDetailInfo struct {
ActID int64 `json:"act_id"`
Count int `json:"count"`
MtCharge float64 `json:"mtCharge"`
PoiCharge float64 `json:"poiCharge"`
Remark string `json:"remark"`
Type int `json:"type"`
}
type SkuBenefitDetailInfo struct {
ActivityPrice float64 `json:"activityPrice"`
AppFoodCode string `json:"app_food_code"`
BoxNumber float64 `json:"boxNumber"`
BoxPrice float64 `json:"boxPrice"`
Count int `json:"count"`
Name string `json:"name"`
OriginPrice float64 `json:"originPrice"`
SkuID string `json:"sku_id"`
TotalActivityPrice float64 `json:"totalActivityPrice"`
TotalBoxPrice float64 `json:"totalBoxPrice"`
TotalMtCharge float64 `json:"totalMtCharge"`
TotalOriginPrice float64 `json:"totalOriginPrice"`
TotalPoiCharge float64 `json:"totalPoiCharge"`
TotalReducePrice float64 `json:"totalReducePrice"`
WmAppOrderActDetails []*WmAppOrderActDetailInfo `json:"wmAppOrderActDetails"`
}
type ActOrderChargeInfo struct {
Comment string `json:"comment"`
FeeTypeDesc string `json:"feeTypeDesc"`
FeeTypeID int `json:"feeTypeId"`
MoneyCent int64 `json:"moneyCent"`
}
type PoiReceiveDetailInfo struct {
ActOrderChargeByMt []*ActOrderChargeInfo `json:"actOrderChargeByMt"`
ActOrderChargeByPoi []*ActOrderChargeInfo `json:"actOrderChargeByPoi"`
FoodShareFeeChargeByPoi int64 `json:"foodShareFeeChargeByPoi"`
LogisticsFee int64 `json:"logisticsFee"`
OnlinePayment int64 `json:"onlinePayment"`
WmPoiReceiveCent int64 `json:"wmPoiReceiveCent"`
}
type OrderInfo struct {
AppOrderCode string `json:"app_order_code"`
AppPoiCode string `json:"app_poi_code"`
AvgSendTime int `json:"avg_send_time"`
BackupRecipientPhone string `json:"backup_recipient_phone"`
Caution string `json:"caution"`
CityID int `json:"city_id"`
Ctime int64 `json:"ctime"`
DaySeq int `json:"day_seq"`
DeliveryTime int `json:"delivery_time"`
Detail string `json:"detail"`
DetailList []*FoodInfo `json:"detailList"`
DinnersNumber int `json:"dinners_number"`
ExpectDeliverTime int `json:"expect_deliver_time"`
Extras string `json:"extras"`
ExtraList []*OrderExtraInfo `json:"extraList"`
HasInvoiced int `json:"has_invoiced"`
InvoiceTitle string `json:"invoice_title"`
IsFavorites bool `json:"is_favorites"`
IsPoiFirstOrder bool `json:"is_poi_first_order"`
IsPre int `json:"is_pre"`
IsThirdShipping int `json:"is_third_shipping"`
Latitude float64 `json:"latitude"`
LogisticsCode string `json:"logistics_code"`
Longitude float64 `json:"longitude"`
OrderCompletedTime int `json:"order_completed_time"`
OrderConfirmTime int `json:"order_confirm_time"`
OrderID int64 `json:"order_id"`
OrderSendTime int `json:"order_send_time"`
OriginalPrice float64 `json:"original_price"`
PackageBagMoney int `json:"package_bag_money"`
PayType int `json:"pay_type"`
PickType int `json:"pick_type"`
PoiReceiveDetail string `json:"poi_receive_detail"`
PoiReceiveDetailPtr *PoiReceiveDetailInfo `json:"poi_receive_detail_ptr"`
RecipientAddress string `json:"recipient_address"`
RecipientName string `json:"recipient_name"`
RecipientPhone string `json:"recipient_phone"`
Remark string `json:"remark"`
Result string `json:"result"`
ShipperPhone string `json:"shipper_phone"`
ShippingFee float64 `json:"shipping_fee"`
ShippingType int `json:"shipping_type"`
SkuBenefitDetail string `json:"sku_benefit_detail"`
SkuBenefitDetailList []*SkuBenefitDetailInfo `json:"sku_benefit_detail_list"`
SourceID int `json:"source_id"`
Status int `json:"status"`
TaxpayerID string `json:"taxpayer_id"`
Total float64 `json:"total"`
Utime int64 `json:"utime"`
WmOrderIDView int64 `json:"wm_order_id_view"`
WmPoiAddress string `json:"wm_poi_address"`
WmPoiID int `json:"wm_poi_id"`
WmPoiName string `json:"wm_poi_name"`
WmPoiPhone string `json:"wm_poi_phone"`
}
type GetOrderActDetailParamAct struct {
Type int `json:"type,omitempty"`
ActID int64 `json:"act_id,omitempty"`
}
type GetOrderActDetailParam struct {
OrderID int64 `json:"order_id_view"`
ActParam []*GetOrderActDetailParamAct `json:"act_param,omitempty"`
}
type OrderActInfo struct {
OrderID int64 `json:"order_id_view"`
ActDetailList []*struct {
ActID int64 `json:"act_id"`
CouponDetail interface{} `json:"coupon_detail"`
EndTime int64 `json:"end_time"`
GiftsDetail interface{} `json:"gifts_detail"`
PoiDetail interface{} `json:"poi_detail"`
ProductDetail struct {
AppFoodCode string `json:"app_food_code"`
CategorySeqence int `json:"category_seqence"`
Charge string `json:"charge"`
DayLimit int `json:"day_limit"`
Name string `json:"name"`
OrderLimit int `json:"order_limit"`
Period string `json:"period"`
Policy string `json:"policy"`
SettingType int `json:"setting_type"`
UserType int `json:"user_type"`
WeeksTime string `json:"weeks_time"`
} `json:"product_detail"`
Remark string `json:"remark"`
StartTime int64 `json:"start_time"`
Status int `json:"status"`
Type int `json:"type"`
TypeName string `json:"type_name"`
} `json:"act_detail_list"`
}
func (a *API) OrderReceived(orderID int64) (err error) {
_, err = a.AccessAPI("order/poi_received", true, map[string]interface{}{
KeyOrderID: orderID,
@@ -115,10 +351,10 @@ func (a *API) OrderRefundReject(orderID int64, reason string) (err error) {
}
func (a *API) OrderApplyPartRefund(orderID int64, reason string, removeSkuList []*RefundSku) (err error) {
_, err = a.AccessAPI("order/applyPartRefund", true, map[string]interface{}{
_, err = a.AccessAPI("order/applyPartRefund", false, map[string]interface{}{
KeyOrderID: orderID,
"reason": reason,
"food_data": removeSkuList,
"food_data": string(utils.MustMarshal(removeSkuList)),
})
return err
}
@@ -128,7 +364,6 @@ func (a *API) OrderViewStatus(orderID int64) (status int, err error) {
KeyOrderID: orderID,
})
if err == nil {
// baseapi.SugarLogger.Debug(result)
return int(utils.MustInterface2Int64(result.(map[string]interface{})["status"])), nil
}
return 0, err
@@ -148,6 +383,50 @@ func (a *API) OrderGetOrderDetail(orderID int64, isMTLogistics bool) (orderInfo
return nil, err
}
func (a *API) OrderGetOrderDetail2(orderID int64, isMTLogistics bool) (orderInfo *OrderInfo, err error) {
orderMap, err := a.OrderGetOrderDetail(orderID, isMTLogistics)
if err == nil {
if err = utils.Map2StructByJson(orderMap, &orderInfo, false); err == nil {
if orderInfo.Detail != "" {
err = utils.UnmarshalUseNumber([]byte(orderInfo.Detail), &orderInfo.DetailList)
}
if err != nil {
return nil, err
}
if orderInfo.Extras != "" {
err = utils.UnmarshalUseNumber([]byte(orderInfo.Extras), &orderInfo.ExtraList)
}
if err != nil {
return nil, err
}
if orderInfo.SkuBenefitDetail != "" {
err = utils.UnmarshalUseNumber([]byte(orderInfo.SkuBenefitDetail), &orderInfo.SkuBenefitDetailList)
}
if err != nil {
return nil, err
}
if orderInfo.PoiReceiveDetail != "" {
err = utils.UnmarshalUseNumber([]byte(orderInfo.PoiReceiveDetail), &orderInfo.PoiReceiveDetailPtr)
}
}
}
return orderInfo, err
}
func (a *API) OrderGetPartRefundFoods(orderID int64) (canRefundFoodList []*CanRefundFoodInfo, err error) {
params := map[string]interface{}{
KeyOrderID: orderID,
}
result, err := a.AccessAPI("order/getPartRefundFoods", true, params)
if err == nil {
err = utils.Map2StructByJson(result, &canRefundFoodList, false)
}
return canRefundFoodList, err
}
func (a *API) OrderLogisticsPush(orderID int64, reason string) (err error) {
_, err = a.AccessAPI("order/logistics/push", true, map[string]interface{}{
KeyOrderID: orderID,
@@ -195,3 +474,37 @@ func (a *API) OrderLogisticsChange2Self(orderID int64) (err error) {
})
return err
}
// refundOrderDetailList不能定义为[]*RefundOrderDetail否则会有解析错误原因不明
func (a *API) GetOrderRefundDetail(orderID int64, refundType int) (refundOrderDetailList []*RefundOrderDetail, err error) {
params := map[string]interface{}{
"wm_order_id_view": orderID,
}
if refundType > 0 {
params["refund_type"] = refundType
}
result, err := a.AccessAPI("ecommerce/order/getOrderRefundDetail", true, params)
if err == nil {
if err = utils.Map2StructByJson(result, &refundOrderDetailList, false); err == nil {
for _, v := range refundOrderDetailList {
if v.Pictures != "" {
if err = utils.UnmarshalUseNumber([]byte(v.Pictures), &v.PictureList); err != nil {
return nil, err
}
}
}
}
}
return refundOrderDetailList, err
}
func (a *API) GetOrderActDetail(queryData []*GetOrderActDetailParam) (orderActList []*OrderActInfo, err error) {
params := map[string]interface{}{
"query_data": string(utils.MustMarshal(queryData)),
}
result, err := a.AccessAPI("ecommerce/order/getOrderActDetail", true, params)
if err == nil {
err = utils.Map2StructByJson(result, &orderActList, false)
}
return orderActList, err
}

View File

@@ -2,6 +2,8 @@ package mtwmapi
import (
"testing"
"git.rosy.net.cn/baseapi/utils"
)
func TestOrderViewStatus(t *testing.T) {
@@ -13,14 +15,56 @@ func TestOrderViewStatus(t *testing.T) {
}
func TestOrderGetOrderDetail(t *testing.T) {
result, err := api.OrderGetOrderDetail(33762863658107006, false)
result, err := api.OrderGetOrderDetail(69760842061320598, false)
if err != nil {
t.Fatal(err)
}
if len(result) == 0 {
t.Fatal("result should have value")
}
// t.Log(utils.Format4Output(result, false))
t.Log(utils.Format4Output(result, false))
}
func TestOrderGetOrderDetail2(t *testing.T) {
result, err := api.OrderGetOrderDetail2(69760842061320598, false)
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}
func TestOrderGetPartRefundFoods(t *testing.T) {
result, err := api.OrderGetPartRefundFoods(25236870562694625)
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}
func TestOrderReceived(t *testing.T) {
err := api.OrderReceived(25236872740350976)
if err != nil {
t.Fatal(err)
}
}
func TestOrderConfirm(t *testing.T) {
err := api.OrderConfirm(25236872740350976)
if err != nil {
t.Fatal(err)
}
}
func TestOrderApplyPartRefund(t *testing.T) {
err := api.OrderApplyPartRefund(25236872740350976, "缺货", []*RefundSku{
&RefundSku{
AppFoodCode: "30831",
Count: 1,
},
})
if err != nil {
t.Fatal(err)
}
}
func TestOrderLogisticsStatus(t *testing.T) {
@@ -45,9 +89,27 @@ func TestOrderBatchPullPhoneNumber(t *testing.T) {
// t.Log(utils.Format4Output(result, false))
}
func TestOrderConfirm(t *testing.T) {
err := api.OrderConfirm(24617230733961993)
func TestGetOrderRefundDetail(t *testing.T) {
result, err := api.GetOrderRefundDetail(25236870166465610, RefundTypePart)
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}
func TestGetOrderActDetaill(t *testing.T) {
result, err := api.GetOrderActDetail([]*GetOrderActDetailParam{
&GetOrderActDetailParam{
OrderID: 69760842061320598,
// ActParam: []*GetOrderActDetailParamAct{
// &GetOrderActDetailParamAct{
// Type: 1,
// },
// },
},
})
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}

View File

@@ -15,6 +15,38 @@ type PoiCategoryInfo struct {
Name string `json:"name"`
}
type PoiInfo struct {
Address string `json:"address,omitempty"`
AppID int `json:"app_id,omitempt"`
AppPoiCode string `json:"app_poi_code,omitempt"`
CityID int `json:"city_id,omitempt"`
Ctime int64 `json:"ctime,omitempt"`
InvoiceDescription string `json:"invoice_description,omitempt"`
InvoiceMinPrice int `json:"invoice_min_price,omitempt"`
InvoiceSupport int `json:"invoice_support,omitempt"`
IsOnline int `json:"is_online,omitempt"`
Latitude float64 `json:"latitude,omitempt"`
LocationID int `json:"location_id,omitempt"`
Longitude float64 `json:"longitude,omitempt"`
Name string `json:"name,omitempt"`
OpenLevel int `json:"open_level,omitempt"`
Phone string `json:"phone,omitempt"`
PicURL string `json:"pic_url,omitempt"`
PicURLLarge string `json:"pic_url_large,omitempt"`
PreBook int `json:"pre_book,omitempt"`
PreBookMaxDays int `json:"pre_book_max_days,omitempt"`
PreBookMinDays int `json:"pre_book_min_days,omitempt"`
PromotionInfo string `json:"promotion_info,omitempt"`
Remark string `json:"remark,omitempt"`
ShippingFee float64 `json:"shipping_fee,omitempt"`
ShippingTime string `json:"shipping_time,omitempt"`
StandbyTel string `json:"standby_tel,omitempt"`
TagName string `json:"tag_name,omitempt"`
ThirdTagName string `json:"third_tag_name,omitempt"`
TimeSelect int `json:"time_select,omitempt"`
Utime int64 `json:"utime,omitempt"`
}
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
@@ -33,17 +65,17 @@ func (a *API) PoiGetIDs() (ids []string, err error) {
return nil, err
}
func (a *API) PoiMGet(poiCodes []string) (pois []map[string]interface{}, err error) {
func (a *API) PoiMGet(poiCodes []string) (pois []*PoiInfo, 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
err = utils.Map2StructByJson(result, &pois, false)
}
return nil, err
return pois, err
}
func (a *API) PoiGet(poiCode string) (poi map[string]interface{}, err error) {
func (a *API) PoiGet(poiCode string) (poi *PoiInfo, err error) {
result, err := a.PoiMGet([]string{poiCode})
if err == nil {
if len(result) == 0 {

View File

@@ -7,7 +7,7 @@ import (
)
const (
testPoiCode = "100077"
testPoiCode = "2523687"
)
func TestPoiGetIDs(t *testing.T) {
@@ -29,7 +29,7 @@ func TestPoiMGet(t *testing.T) {
if len(result) != 1 {
t.Fatal("result len is not ok")
}
if result[0][KeyAppPoiCode] != testPoiCode {
if result[0].AppPoiCode != testPoiCode {
t.Fatal("test_poi_01 is not equal")
}
t.Log(utils.Format4Output(result, false))
@@ -40,7 +40,7 @@ func TestPoiSave(t *testing.T) {
if err != nil {
t.Fatal(err)
}
poiParams := utils.FilterMapNilMembers(result[0])
poiParams := utils.FilterMapNilMembers(utils.Struct2FlatMap(result[0]))
poiParams["name"] = "中国"
err = api.PoiSave(testPoiCode, poiParams)
if err != nil {

View File

@@ -6,6 +6,10 @@ import (
"git.rosy.net.cn/baseapi/utils"
)
const (
MaxSkuNameCharCount = 30 // SkuName的最大字符数
)
type RetailCategoryInfo struct {
Name string `json:"name"`
Sequence int `json:"sequence"`
@@ -20,6 +24,68 @@ type RetailTag struct {
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"`
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为空
@@ -100,48 +166,59 @@ func (a *API) RetailBatchInitData(poiCode string, foodDataList []map[string]inte
return err
}
func (a *API) RetailSkuPrice(poiCode string, foodData []map[string]interface{}) (err error) {
func (a *API) RetailSkuPrice(poiCode string, foodData []*BareStoreFoodInfo) (err error) {
_, err = a.AccessAPI("retail/sku/price", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
"food_data": foodData,
"food_data": string(utils.MustMarshal(foodData)),
})
return err
}
func (a *API) RetailSkuStock(poiCode string, foodData []map[string]interface{}) (err error) {
func (a *API) RetailSkuStock(poiCode string, foodData []*BareStoreFoodInfo) (err error) {
_, err = a.AccessAPI("retail/sku/stock", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
"food_data": foodData,
"food_data": string(utils.MustMarshal(foodData)),
})
return err
}
func (a *API) RetailGet(poiCode, foodCode string) (food map[string]interface{}, err error) {
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 {
return result.(map[string]interface{}), nil
if err = utils.Map2StructByJson(result, &food, true); err == nil && food.Skus != "" {
err = utils.UnmarshalUseNumber([]byte(food.Skus), &food.SkuList)
}
}
return nil, err
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": standardSkus,
"unstandard_skus": unstandardSkus,
"standard_skus": string(utils.MustMarshal(standardSkus)),
"unstandard_skus": string(utils.MustMarshal(unstandardSkus)),
})
return err
}
func (a *API) RetailSkuSellStatus(poiCode string, foodData []map[string]interface{}, status int) (err error) {
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": foodData,
"status": status,
"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
}

View File

@@ -47,7 +47,15 @@ func TestRetailList(t *testing.T) {
if len(result) == 0 {
t.Fatal("should have items")
}
// t.Log(utils.Format4Output(result, false))
t.Log(utils.Format4Output(result, false))
}
func TestRetailGet(t *testing.T) {
result, err := api.RetailGet(testPoiCode, "5246")
if err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(result, false))
}
func TestRetailGetSpTagIds(t *testing.T) {
@@ -69,7 +77,7 @@ func TestRetailCatSkuBatchDelete(t *testing.T) {
}
func TestRetailDelete(t *testing.T) {
err := api.RetailDelete(testPoiCode, "")
err := api.RetailDelete(testPoiCode, "614")
if err != nil {
t.Fatal(err)
}

View File

@@ -17,10 +17,9 @@ import (
)
const (
DefClientTimeout = 10 * time.Second
DefClientTimeout = 5 * time.Second
DefSleepSecondWhenExceedLimit = 3 * time.Second
DefRandSlice = 10
DefMaxRecoverableRetryCount = 3
DefMaxRecoverableRetryCount = 1
DefMaxExceedLimitRetryCount = 25
)
@@ -89,7 +88,7 @@ func getClonedData(requestURL *url.URL, r *bytes.Buffer) string {
return retVal
}
func AccessPlatformAPIWithRetry(client *http.Client, handleRequest func() *http.Request, config *APIConfig, handleResponse func(bodyMap map[string]interface{}) (string, error)) error {
func AccessPlatformAPIWithRetry(client *http.Client, handleRequest func() *http.Request, config *APIConfig, handleResponse func(response *http.Response, bodyMap map[string]interface{}) (string, error)) error {
exceedLimitRetryCount := 0
recoverableErrorRetryCount := 0
for {
@@ -106,8 +105,8 @@ func AccessPlatformAPIWithRetry(client *http.Client, handleRequest func() *http.
if err != nil {
baseapi.SugarLogger.Debugf("AccessPlatformAPIWithRetry:%s client.Get return err:%v", trackID, err)
err, ok := err.(net.Error)
recoverableErrorRetryCount++
if ok /*&& err.Timeout()*/ && recoverableErrorRetryCount <= config.MaxRecoverableRetryCount { // 只要是网络错误都重试
recoverableErrorRetryCount++
continue
} else {
baseapi.SugarLogger.Errorf("AccessPlatformAPIWithRetry:%s access api url:%v, request:%v, error:%v", trackID, request.URL, getClonedData(request.URL, savedBuf), err)
@@ -155,7 +154,7 @@ func AccessPlatformAPIWithRetry(client *http.Client, handleRequest func() *http.
if err != nil {
errLevel = ErrLevelRecoverableErr // 读取数据错误,或数据格式错误认为是偶发情况,重试
} else {
errLevel, err = handleResponse(bodyMap)
errLevel, err = handleResponse(response, bodyMap)
}
if err == nil {
return nil

View File

@@ -32,14 +32,29 @@ type PendingSaveB2CSkuVo struct {
type PendingSaveSkuVo struct {
OuterSkuCode string `json:"outerSkuCode"`
ImageURL string `json:"imageUrl"`
SalePrice float32 `json:"salePrice"`
OriginalPrice float32 `json:"originalPrice"`
CostPrice float32 `json:"costPrice"`
ImageURL string `json:"imageUrl,omitempty"`
SalePrice float64 `json:"salePrice"`
OriginalPrice float64 `json:"originalPrice,omitempty"`
CostPrice float64 `json:"costPrice"`
EditStockNum int `json:"editStockNum"`
B2cSku *PendingSaveB2CSkuVo `json:"b2cSku"`
}
type PendingSaveGoodsVo struct {
Title string `json:"title"`
OuterGoodsCode string `json:"outerGoodsCode,omitempty"`
IsMultiSku int `json:"isMultiSku"`
GoodsImageURL []string `json:"goodsImageUrl"`
GoodsDesc string `json:"goodsDesc"`
InitialSales int `json:"initialSales,omitempty"`
DeductStockType int `json:"deductStockType"`
IsPutAway int `json:"isPutAway"`
Sort int `json:"Sort,omitempty"`
CategoryID int64 `json:"categoryId"`
B2cGoods []*PendingSaveB2CGoodsVo `json:"b2cGoods"`
SkuList []*PendingSaveSkuVo `json:"skuList"`
}
type Category struct {
CategoryID int64 `json:"categoryId"`
Title string `json:"title"`
@@ -198,6 +213,16 @@ func (a *API) UpdateClassify(classifyID int64, title string, imageURL string) (e
return err
}
func getDataFromCUGoodsResult(result interface{}) (goodsId int64, skuMap map[string]int64, err error) {
skuMap = make(map[string]int64)
skuList := result.(map[string]interface{})["skuList"].([]interface{})
for _, v := range skuList {
sku := v.(map[string]interface{})
skuMap[utils.Interface2String(sku[KeyOuterSkuCode])] = utils.MustInterface2Int64(sku[KeySkuID])
}
return utils.MustInterface2Int64(result.(map[string]interface{})["goodsId"]), skuMap, nil
}
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,
@@ -221,13 +246,20 @@ func (a *API) AddGoods(outerGoodsCode, title string, isMultiSku bool, goodsImage
"goods": mergedMap,
})
if err == nil {
skuMap := make(map[string]int64)
skuList := result.(map[string]interface{})["skuList"].([]interface{})
for _, v := range skuList {
sku := v.(map[string]interface{})
skuMap[utils.Interface2String(sku[KeyOuterSkuCode])] = utils.MustInterface2Int64(sku[KeySkuID])
}
return utils.MustInterface2Int64(result.(map[string]interface{})["goodsId"]), skuMap, nil
return getDataFromCUGoodsResult(result)
}
return 0, nil, err
}
func (a *API) AddGoods2(goodsInfo *PendingSaveGoodsVo) (goodsId int64, skuMap map[string]int64, err error) {
if goodsInfo.DeductStockType == 0 {
goodsInfo.DeductStockType = DeductStockTypePay
}
result, err := a.AccessAPI("goods/addGoods", map[string]interface{}{
"goods": goodsInfo,
})
if err == nil {
return getDataFromCUGoodsResult(result)
}
return 0, nil, err
}
@@ -254,13 +286,20 @@ func (a *API) UpdateGoods(goodsID int64, title string, isMultiSku bool, goodsIma
"goods": mergedMap,
})
if err == nil {
skuMap := make(map[string]int64)
skuList := result.(map[string]interface{})["skuList"].([]interface{})
for _, v := range skuList {
sku := v.(map[string]interface{})
skuMap[utils.Interface2String(sku[KeyOuterSkuCode])] = utils.MustInterface2Int64(sku[KeySkuID])
}
return utils.MustInterface2Int64(result.(map[string]interface{})["goodsId"]), skuMap, nil
return getDataFromCUGoodsResult(result)
}
return 0, nil, err
}
func (a *API) UpdateGoods2(goodsInfo *PendingSaveGoodsVo) (goodsId int64, skuMap map[string]int64, err error) {
if goodsInfo.DeductStockType == 0 {
goodsInfo.DeductStockType = DeductStockTypePay
}
result, err := a.AccessAPI("goods/updateGoods", map[string]interface{}{
"goods": goodsInfo,
})
if err == nil {
return getDataFromCUGoodsResult(result)
}
return 0, nil, err
}

View File

@@ -162,7 +162,7 @@ func (a *API) AccessAPI(apiStr string, apiParams map[string]interface{}) (retVal
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (errLevel string, err error) {
func(response *http.Response, jsonResult1 map[string]interface{}) (errLevel string, err error) {
// baseapi.SugarLogger.Debug(utils.Format4Output(jsonResult1, false))
code := ResponseCodeSuccess
errMsg := ""

View File

@@ -100,7 +100,7 @@ func (a *API) AccessAPI(action string, params map[string]interface{}, body strin
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (result string, err error) {
func(response *http.Response, jsonResult1 map[string]interface{}) (result string, err error) {
var errInfo *ErrorInfo
// 微信的返回值,在错误与正常情况下,结构是完全不一样的
if errCode, ok := jsonResult1["errcode"]; ok {

View File

@@ -80,7 +80,7 @@ func (a *API) AccessAPI(apiName string, apiParams map[string]interface{}) (retVa
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (errLevel string, err error) {
func(response *http.Response, jsonResult1 map[string]interface{}) (errLevel string, err error) {
code := int(utils.Interface2Int64WithDefault(jsonResult1["errno"], ResponseCodeSuccess))
if code == ResponseCodeSuccess {
retVal = jsonResult1

View File

@@ -91,7 +91,7 @@ func (a *API) AccessAPI(apiName string, apiParams map[string]interface{}) (retVa
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (errLevel string, err error) {
func(response *http.Response, jsonResult1 map[string]interface{}) (errLevel string, err error) {
code := utils.Interface2String(jsonResult1["error"])
if code == ResponseCodeSuccess {
retVal, _ = jsonResult1["body"].(map[string]interface{})

View File

@@ -88,7 +88,7 @@ func (a *API) AccessAPI(apiName string, apiParams map[string]interface{}, isGet
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (errLevel string, err error) {
func(response *http.Response, jsonResult1 map[string]interface{}) (errLevel string, err error) {
code := int(utils.MustInterface2Int64(jsonResult1["errNum"]))
if code == ResponseCodeSuccess {
retVal, _ = jsonResult1["retData"].(map[string]interface{})

47
utils/java_date.go Normal file
View File

@@ -0,0 +1,47 @@
package utils
import (
"time"
)
type JavaDate struct {
Day time.Weekday `json:"day"` // day of the week
Hours int `json:"hours"`
Minutes int `json:"minutes"`
Seconds int `json:"seconds"`
Time int64 `json:"time"`
TimezoneOffset int `json:"timezoneOffset"`
Date int `json:"date"`
Month time.Month `json:"month"`
Year int `json:"year"`
}
func NewJavaDate() *JavaDate {
return NewJavaDateFromTime(time.Now())
}
func NewJavaDateFromTime(tm time.Time) (jd *JavaDate) {
jd = &JavaDate{
Time: tm.UnixNano() / 1000000,
Day: tm.Weekday(),
Hours: tm.Hour(),
Minutes: tm.Minute(),
Seconds: tm.Second(),
}
jd.Year, jd.Month, jd.Date = tm.Date()
jd.Year -= 1900
jd.Month--
_, jd.TimezoneOffset = tm.Zone()
jd.TimezoneOffset /= 60
jd.TimezoneOffset = -jd.TimezoneOffset
return jd
}
func (j *JavaDate) GoTime() time.Time {
if j == nil {
return DefaultTimeValue
}
return Timestamp2Time(j.Time / 1000)
}

38
utils/java_date_test.go Normal file
View File

@@ -0,0 +1,38 @@
package utils
import (
"testing"
)
func TestJavaDate(t *testing.T) {
var (
date interface{}
jd *JavaDate
)
err := UnmarshalUseNumber([]byte(`
{
"date":6,
"day":1,
"hours":9,
"minutes":45,
"month":4,
"seconds":46,
"time":1557107146000,
"timezoneOffset":-480,
"year":119
}
`), &date)
if err != nil {
t.Fatal(err)
}
err = Map2StructByJson(date, &jd, false)
if err != nil {
t.Fatal(err)
}
t.Log(jd.GoTime())
t.Log(Format4Output(jd, false))
jd = NewJavaDateFromTime(jd.GoTime())
t.Log(jd.GoTime())
t.Log(Format4Output(jd, false))
}

View File

@@ -5,6 +5,8 @@ import (
"encoding/binary"
"fmt"
"runtime/debug"
"git.rosy.net.cn/baseapi/utils"
)
const (
@@ -65,23 +67,27 @@ func taskFun(taskChan chan *TaskParam, index int) {
}
}
func (p *Pool) callFun(func4Call func(), primaryID string, isAsync bool) chan interface{} {
func (p *Pool) callFun(func4Call func(), primaryID string, isAsync bool) (r interface{}) {
result := md5.Sum([]byte(primaryID))
resultInt64 := int64(binary.LittleEndian.Uint32(result[8:]))
chanIndex := int(resultInt64 % int64(p.curRoutineCount))
chanParam := &TaskParam{
handler: func4Call,
}
if isAsync {
if !isAsync {
chanParam.resultChan = make(chan interface{})
}
p.taskChans[chanIndex] <- chanParam
return chanParam.resultChan
if !isAsync {
r = <-chanParam.resultChan
close(chanParam.resultChan)
}
return r
}
func (p *Pool) CallFun(func4Call func(), primaryID string) {
if p.curRoutineCount > 0 {
r := <-p.callFun(func4Call, primaryID, true)
r := p.callFun(func4Call, primaryID, false)
if r != nil {
panic(r)
}
@@ -94,5 +100,7 @@ func (p *Pool) CallFunAsync(func4Call func(), primaryID string) {
if p.curRoutineCount == 0 {
panic("CallFunAsync can not run when p.curRoutineCount == 0")
}
p.callFun(func4Call, primaryID, false)
utils.CallFuncAsync(func() {
p.callFun(func4Call, primaryID, true)
})
}

View File

@@ -7,7 +7,6 @@ import (
"fmt"
"math"
"net/url"
"reflect"
"strconv"
"strings"
"time"
@@ -22,17 +21,6 @@ var (
ZeroTimeValue = time.Time{}
)
func GetConcretValue(value reflect.Value) reflect.Value {
for {
if value.Kind() == reflect.Interface || value.Kind() == reflect.Ptr {
value = value.Elem()
} else {
break
}
}
return value
}
func UnmarshalUseNumber(data []byte, result interface{}) error {
err := TryUnmarshalUseNumber(data, result)
if err != nil {
@@ -184,21 +172,6 @@ func Interface2StringList(data interface{}) []string {
return retVal
}
func Interface2Slice(value interface{}) (retVal []interface{}) {
typeInfo := reflect.TypeOf(value)
if typeInfo.Kind() != reflect.Slice {
panic("list must be slice type!")
}
if value != nil {
valueInfo := reflect.ValueOf(value)
retVal = make([]interface{}, valueInfo.Len())
for i := 0; i < valueInfo.Len(); i++ {
retVal[i] = valueInfo.Index(i).Interface()
}
}
return retVal
}
func Slice2MapSlice(data []interface{}) []map[string]interface{} {
retVal := make([]map[string]interface{}, len(data))
for k, v := range data {
@@ -486,6 +459,7 @@ func Struct2FlatMap(obj interface{}) map[string]interface{} {
return FlatMap(m)
}
// !!! 此函数好像不支持struct是内嵌结构的
func Map2StructByJson(inObj interface{}, outObjAddr interface{}, weaklyTypedInput bool) (err error) {
decoder, _ := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
TagName: "json",

View File

@@ -14,6 +14,10 @@ import (
uuid "github.com/satori/go.uuid"
)
const (
APIPin = "jxadmin"
)
func DictKeysMan(data interface{}, keysToRemove []string, keysToKeep []string) interface{} {
if data == nil || (keysToKeep == nil && keysToRemove == nil) {
return data
@@ -101,10 +105,11 @@ func GetCurTimestamp() int64 {
}
func GetAPIOperator(userName string) string {
retVal := "jxc4-admin"
retVal := APIPin
if userName != "" {
retVal += "-" + userName
}
retVal = LimitUTF8StringLen(retVal, 50)
return retVal
}
@@ -182,14 +187,18 @@ func GenerateGetURL(baseURL, apiStr string, params map[string]interface{}) strin
return baseURL + queryString
}
func SendFakeRequest(method, url, body, contentType string) (*http.Response, error) {
request, _ := http.NewRequest(method, url, strings.NewReader(body))
func BuildRequest(method, url, body, contentType string) (request *http.Request) {
request, _ = http.NewRequest(method, url, strings.NewReader(body))
if contentType == "" {
contentType = "application/x-www-form-urlencoded"
}
request.Header.Set("Content-Type", contentType)
return request
}
func SendFakeRequest(method, url, body, contentType string) (*http.Response, error) {
client := &http.Client{}
return client.Do(request)
return client.Do(BuildRequest(method, url, body, contentType))
}
// 过滤 utf8mb4比如emoji表情
@@ -238,7 +247,7 @@ func Base64DecodeMultiString(strs ...string) (decodedData [][]byte, err error) {
return decodedData, nil
}
// 只适合纯英文的情况
// 限制的是字节数,只适合纯英文的情况推荐使用LimitMixedStringLen除非确定是纯英文且对性能相当敏感
func LimitStringLen(str string, maxByteCount int) (limitedStr string) {
if maxByteCount > 0 {
if strLen := len(str); strLen > maxByteCount {

85
utils/utils_reflect.go Normal file
View File

@@ -0,0 +1,85 @@
package utils
import (
"reflect"
"strings"
)
// 得到一个结构体(或指向结构体的指针)中其成员(tag指定缺省是字段名)的路径(嵌入结构体或指向结构指针会被展开)
func GetStructObjNameIndex(obj interface{}, tag string) map[string][]int {
objType := reflect.TypeOf(obj)
return GetStructNameIndex(objType, tag)
}
func GetStructNameIndex(objType reflect.Type, tag string) (nameIndex map[string][]int) {
if objType.Kind() == reflect.Ptr {
objType = objType.Elem()
}
if objType.Kind() != reflect.Struct {
panic("obj must be struct obj or pointer to struct of obj!")
}
nameIndex = make(map[string][]int)
for i := 0; i < objType.NumField(); i++ {
structField := objType.Field(i)
fieldType := structField.Type
if IndirectType(fieldType).Kind() == reflect.Struct && IsFieldEmbedded(&structField) { // 嵌入
subNameIndex := GetStructNameIndex(fieldType, tag)
for k, v := range subNameIndex {
nameIndex[k] = append([]int{i}, v...)
}
} else {
tagName := structField.Name
if tag != "" {
tagName2 := strings.Split(structField.Tag.Get(tag), ",")[0]
if tagName2 == "-" {
tagName = "" // 忽略
} else if tagName2 != "" {
tagName = tagName2
}
}
if tagName != "" {
nameIndex[tagName] = []int{i}
}
}
}
return nameIndex
}
// 判断成员是否是嵌入的,依据是成员名与类型名相同
func IsFieldEmbedded(structField *reflect.StructField) bool {
return structField.Name == IndirectType(structField.Type).Name()
}
func IndirectType(objType reflect.Type) reflect.Type {
if objType.Kind() == reflect.Ptr {
objType = objType.Elem()
}
return objType
}
func GetConcretValue(value reflect.Value) reflect.Value {
for {
if value.Kind() == reflect.Interface || value.Kind() == reflect.Ptr {
value = value.Elem()
} else {
break
}
}
return value
}
func Interface2Slice(value interface{}) (retVal []interface{}) {
typeInfo := reflect.TypeOf(value)
if typeInfo.Kind() != reflect.Slice {
panic("list must be slice type!")
}
if value != nil {
valueInfo := reflect.ValueOf(value)
retVal = make([]interface{}, valueInfo.Len())
for i := 0; i < valueInfo.Len(); i++ {
retVal[i] = valueInfo.Index(i).Interface()
}
}
return retVal
}

View File

@@ -0,0 +1,24 @@
package utils
import (
"reflect"
"testing"
)
func TestReflect(t *testing.T) {
type SubXXXX struct {
IntData int
StrData string
}
type XXXX struct {
SubXXXX
A int
B string `json:"b"`
}
x := XXXX{}
typeX := reflect.TypeOf(x)
field := typeX.Field(0)
t.Log(IsFieldEmbedded(&field))
t.Log(Format4Output(GetStructObjNameIndex(&x, ""), false))
}