diff --git a/platformapi/autonavi/autonavi.go b/platformapi/autonavi/autonavi.go
index a81ddaaa..9ff7e07c 100644
--- a/platformapi/autonavi/autonavi.go
+++ b/platformapi/autonavi/autonavi.go
@@ -43,6 +43,7 @@ const (
const (
FakeDistrictPadding = 9000000
+ MaxConvertCount = 40
)
var (
@@ -89,6 +90,11 @@ type District struct {
Districts []*District `json:"districts"`
}
+type Coordinate struct {
+ Lng float64 `json:"lng"`
+ Lat float64 `json:"lat"`
+}
+
type ResponseResult map[string]interface{}
type API struct {
@@ -127,6 +133,10 @@ func (a *API) signParams(mapData map[string]interface{}) string {
return fmt.Sprintf("%X", md5.Sum([]byte(finalStr)))
}
+func coordinate2String(lng, lat float64) (str string) {
+ return fmt.Sprintf("%.6f,%.6f", lng, lat)
+}
+
func (a *API) AccessAPI(apiStr string, params map[string]interface{}) (retVal ResponseResult, err error) {
params2 := utils.MergeMaps(utils.Params2Map("key", a.key, "output", "json"), params)
params2[signKey] = a.signParams(params2)
@@ -161,6 +171,15 @@ func (a *API) AccessAPI(apiStr string, params map[string]interface{}) (retVal Re
// 为了方便调用者编码,如果失败,也会返回未转换的原始值
func (a *API) CoordinateConvert(lng, lat float64, coordsys string) (retLng, retLat float64, err error) {
+ // outCoords, err := a.BatchCoordinateConvert([]*Coordinate{
+ // &Coordinate{
+ // Lng: lng,
+ // Lat: lat,
+ // },
+ // }, coordsys)
+ // if err == nil {
+ // retLng, retLat = outCoords[0].Lng, outCoords[0].Lat
+ // }
if coordsys == "" || coordsys == CoordSysAutonavi {
return lng, lat, nil
}
@@ -168,7 +187,7 @@ func (a *API) CoordinateConvert(lng, lat float64, coordsys string) (retLng, retL
return 0.0, 0.0, nil
}
params := map[string]interface{}{
- "locations": fmt.Sprintf("%.6f,%.6f", lng, lat),
+ "locations": coordinate2String(lng, lat),
"coordsys": coordsys,
}
result, err := a.AccessAPI("assistant/coordinate/convert", params)
@@ -180,6 +199,33 @@ func (a *API) CoordinateConvert(lng, lat float64, coordsys string) (retLng, retL
return lng, lat, err
}
+func (a *API) BatchCoordinateConvert(coords []*Coordinate, coordsys string) (outCoords []*Coordinate, err error) {
+ if coordsys == "" || coordsys == CoordSysAutonavi {
+ return coords, nil
+ }
+ var coordsStrList []string
+ for _, v := range coords {
+ coordsStrList = append(coordsStrList, coordinate2String(v.Lng, v.Lat))
+ }
+ params := map[string]interface{}{
+ "locations": strings.Join(coordsStrList, "|"),
+ "coordsys": coordsys,
+ }
+ result, err := a.AccessAPI("assistant/coordinate/convert", params)
+ if err == nil {
+ coordinate := result["locations"].(string)
+ retCoordsStrList := strings.Split(coordinate, ";")
+ for _, v := range retCoordsStrList {
+ pair := strings.Split(v, ",")
+ outCoords = append(outCoords, &Coordinate{
+ Lng: utils.Str2Float64(pair[0]),
+ Lat: utils.Str2Float64(pair[1]),
+ })
+ }
+ }
+ return outCoords, err
+}
+
func (a *API) GetCoordinateFromAddress(address string, cityInfo string) (lng, lat float64, districtCode int) {
params := map[string]interface{}{
"address": address,
@@ -218,7 +264,7 @@ func (a *API) GetCoordinateDistrictCode(lng, lat float64) (districtCode int) {
func (a *API) GetCoordinateAreaInfo(lng, lat float64) (areaInfo map[string]interface{}, err error) {
params := map[string]interface{}{
- "location": fmt.Sprintf("%.6f,%.6f", lng, lat),
+ "location": coordinate2String(lng, lat),
}
result, err := a.AccessAPI("geocode/regeo", params)
return result, err
@@ -283,3 +329,18 @@ func (a *API) getDistrictsFromInterface(districts interface{}) (districtList []*
func GetDistrictLevel(levelName string) (level int) {
return levelStr2IntMap[levelName]
}
+
+// 两点之间的步行距离,单位为米
+func (a *API) WalkingDistance(lng1, lat1, lng2, lat2 float64) (distance float64) {
+ params := map[string]interface{}{
+ "origin": coordinate2String(lng1, lat1),
+ "destination": coordinate2String(lng2, lat2),
+ }
+ result, err := a.AccessAPI("direction/walking", params)
+ if err == nil {
+ if paths, _ := result["route"].(map[string]interface{})["paths"].([]interface{}); len(paths) > 0 {
+ distance = utils.Interface2Float64WithDefault(paths[0].(map[string]interface{})["distance"], 0)
+ }
+ }
+ return distance
+}
diff --git a/platformapi/autonavi/autonavi_test.go b/platformapi/autonavi/autonavi_test.go
index 81c2d857..26fd32a1 100644
--- a/platformapi/autonavi/autonavi_test.go
+++ b/platformapi/autonavi/autonavi_test.go
@@ -19,7 +19,7 @@ func init() {
sugarLogger = logger.Sugar()
baseapi.Init(sugarLogger)
- autonaviAPI = New("4427170f870af2110becb8852d36ab08")
+ autonaviAPI = New("ef64f638f31e05cb7bde28790f7309fe")
}
func TestCoordinateConvert(t *testing.T) {
@@ -88,3 +88,8 @@ func TestGetCoordinateFromAddress(t *testing.T) {
lng, lat, districtCode := autonaviAPI.GetCoordinateFromAddress("天府广场", "成都市")
t.Logf("lng:%f, lat:%f, districtCode:%d", lng, lat, districtCode)
}
+
+func TestWalkingDistance(t *testing.T) {
+ distance := autonaviAPI.WalkingDistance(104.057289, 30.694798, 104.066289, 30.695598)
+ t.Logf("distance:%f", distance)
+}
diff --git a/platformapi/baidunavi/baidunavi.go b/platformapi/baidunavi/baidunavi.go
new file mode 100644
index 00000000..20fde070
--- /dev/null
+++ b/platformapi/baidunavi/baidunavi.go
@@ -0,0 +1,168 @@
+package baidunavi
+
+import (
+ "crypto/md5"
+ "fmt"
+ "net/http"
+ "net/url"
+ "sort"
+ "strings"
+
+ "git.rosy.net.cn/baseapi/platformapi"
+ "git.rosy.net.cn/baseapi/utils"
+)
+
+const (
+ signKey = "sn"
+ resultKey = "result"
+ prodURL = "http://api.map.baidu.com"
+)
+
+const (
+ StatusCodeSuccess = 0
+ StatusCodeInternalErr = 1 // 服务器内部错误
+ StatusCodeExceedDailyQuota = 301 // 永久配额超限,限制访问
+ StatusCodeExceedQuota = 302 // 天配额超限,限制访问
+ StatusCodeExceedDailyConcurrentQuota = 401 // 当前并发量已经超过约定并发配额,限制访问
+ StatusCodeExceedConcurrentQuota = 402 // 当前并发量已经超过约定并发配额,并且服务总并发量也已经超过设定的总并发配额,限制访问
+)
+
+const (
+ CoordSysWGS84 = 1 // GPS设备获取的角度坐标,WGS84坐标
+ CoordSysGCJ02 = 3 // google地图、soso地图、aliyun地图、mapabc地图和amap地图所用坐标,国测局(GCJ02)坐标
+ CoordSysBaiDu = 5 // 百度地图采用的经纬度坐标
+)
+
+const (
+ MaxCoordsConvBatchSize = 100
+)
+
+var (
+ exceedLimitCodes = map[int]int{
+ StatusCodeExceedDailyQuota: 1,
+ StatusCodeExceedQuota: 1,
+ StatusCodeExceedDailyConcurrentQuota: 1,
+ StatusCodeExceedConcurrentQuota: 1,
+ }
+
+ canRetryCodes = map[int]int{
+ StatusCodeInternalErr: 1,
+ }
+)
+
+type Coordinate struct {
+ Lng float64 `json:"x"`
+ Lat float64 `json:"y"`
+}
+
+type API struct {
+ client *http.Client
+ config *platformapi.APIConfig
+ ak string
+ sk string
+}
+
+func New(ak, sk string, config ...*platformapi.APIConfig) *API {
+ curConfig := platformapi.DefAPIConfig
+ if len(config) > 0 {
+ curConfig = *config[0]
+ }
+ return &API{
+ ak: ak,
+ sk: sk,
+ client: &http.Client{Timeout: curConfig.ClientTimeout},
+ config: &curConfig,
+ }
+}
+
+func (a *API) signParams(apiStr string, mapData map[string]interface{}) string {
+ keys := make([]string, 0)
+ for k := range mapData {
+ if k != signKey {
+ keys = append(keys, k)
+ }
+ }
+ sort.Strings(keys)
+
+ strList := []string{}
+ for _, k := range keys {
+ strList = append(strList, k+"="+url.QueryEscape(fmt.Sprint(mapData[k])))
+ }
+ finalStr := "/" + apiStr + "?" + strings.Join(strList, "&") + a.sk
+ // baseapi.SugarLogger.Debugf("sign str:%v", finalStr)
+ finalStr = url.QueryEscape(finalStr)
+ return fmt.Sprintf("%x", md5.Sum([]byte(finalStr)))
+}
+
+func genGetURL(baseURL, apiStr string, params map[string]interface{}) string {
+ keys := make([]string, 0)
+ for k := range params {
+ if k != signKey {
+ keys = append(keys, k)
+ }
+ }
+ sort.Strings(keys)
+
+ strList := []string{}
+ for _, k := range keys {
+ strList = append(strList, k+"="+url.QueryEscape(fmt.Sprint(params[k])))
+ }
+ strList = append(strList, signKey+"="+url.QueryEscape(fmt.Sprint(params[signKey])))
+ queryString := "?" + strings.Join(strList, "&")
+ if apiStr != "" {
+ return baseURL + "/" + apiStr + queryString
+ }
+ return baseURL + queryString
+}
+
+func (a *API) AccessAPI(apiStr string, params map[string]interface{}) (retVal interface{}, err error) {
+ apiStr += "/"
+ params2 := utils.MergeMaps(utils.Params2Map("ak", a.ak, "output", "json"), params)
+ params2[signKey] = a.signParams(apiStr, params2)
+
+ err = platformapi.AccessPlatformAPIWithRetry(a.client,
+ func() *http.Request {
+ request, _ := http.NewRequest(http.MethodGet, genGetURL(prodURL, apiStr, params2), nil)
+ return request
+ },
+ a.config,
+ func(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) {
+ if jsonResult1 == nil {
+ return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil")
+ }
+ status := int(utils.MustInterface2Int64(jsonResult1["status"]))
+ if status == StatusCodeSuccess {
+ retVal = jsonResult1[resultKey]
+ return platformapi.ErrLevelSuccess, nil
+ }
+ newErr := utils.NewErrorIntCode(utils.Interface2String(jsonResult1["message"]), status)
+ if _, ok := exceedLimitCodes[status]; ok {
+ return platformapi.ErrLevelExceedLimit, newErr
+ } else if _, ok := canRetryCodes[status]; ok {
+ return platformapi.ErrLevelRecoverableErr, newErr
+ } else {
+ return platformapi.ErrLevelCodeIsNotOK, newErr
+ }
+ })
+ return retVal, err
+}
+
+func (a *API) BatchCoordinateConvert(coords []*Coordinate, fromCoordSys, toCoordSys int) (outCoords []*Coordinate, err error) {
+ if fromCoordSys == toCoordSys {
+ return coords, nil
+ }
+ var coordsStrList []string
+ for _, v := range coords {
+ coordsStrList = append(coordsStrList, fmt.Sprintf("%.6f,%.6f", v.Lng, v.Lat))
+ }
+ params := map[string]interface{}{
+ "coords": strings.Join(coordsStrList, ";"),
+ "from": fromCoordSys,
+ "to": toCoordSys,
+ }
+ result, err := a.AccessAPI("geoconv/v1", params)
+ if err == nil {
+ err = utils.Map2StructByJson(result, &outCoords, false)
+ }
+ return outCoords, err
+}
diff --git a/platformapi/baidunavi/baidunavi_test.go b/platformapi/baidunavi/baidunavi_test.go
new file mode 100644
index 00000000..3b11f2f4
--- /dev/null
+++ b/platformapi/baidunavi/baidunavi_test.go
@@ -0,0 +1,41 @@
+package baidunavi
+
+import (
+ "testing"
+
+ "git.rosy.net.cn/baseapi"
+ "git.rosy.net.cn/baseapi/utils"
+
+ "go.uber.org/zap"
+)
+
+var (
+ api *API
+ sugarLogger *zap.SugaredLogger
+)
+
+func init() {
+ logger, _ := zap.NewDevelopment()
+ sugarLogger = logger.Sugar()
+ baseapi.Init(sugarLogger)
+
+ api = New("eL94zToVOdGDTkNQxV8dnEQ1ZRcB2UKb", "ZG0OOpOsOVURUwAkkmoHQFKRCbzn0zGb")
+}
+
+func TestBatchCoordinateConvert(t *testing.T) {
+ result, err := api.BatchCoordinateConvert([]*Coordinate{
+ &Coordinate{
+ Lng: 104.057367,
+ Lat: 30.694686,
+ },
+ &Coordinate{
+ Lng: 104.057367,
+ Lat: 30.694686,
+ },
+ }, CoordSysGCJ02, CoordSysBaiDu)
+ if err != nil {
+ t.Fatalf("TestCoordinateConvert failed with error:%v", err)
+ } else {
+ t.Log(utils.Format4Output(result, false))
+ }
+}
diff --git a/platformapi/ebaiapi/activity.go b/platformapi/ebaiapi/activity.go
index 27b8f5b0..4bf6591a 100644
--- a/platformapi/ebaiapi/activity.go
+++ b/platformapi/ebaiapi/activity.go
@@ -145,7 +145,7 @@ func (a *API) ActivitySkuAddBatch(activityID int64, shopID string, baiduShopID i
params[skusKey] = skuList2Str(activityType, skuList, isSkuIDCustom)
result, err := a.AccessAPI("activity.sku.add.batch", params)
if err == nil {
- return strings.Split(result.Data.(string), ","), nil
+ return strings.Split(utils.Interface2String(result.Data), ","), nil
}
return nil, err
}
@@ -180,7 +180,7 @@ func (a *API) ActivitySkuDeleteBatch(activityID int64, shopID string, baiduShopI
params[skusKey] = strings.Join(skuIDs, ",")
result, err := a.AccessAPI("activity.sku.delete.batch", params)
if err == nil {
- return strings.Split(result.Data.(string), ","), nil
+ return strings.Split(utils.Interface2String(result.Data), ","), nil
}
return nil, err
}
@@ -236,7 +236,7 @@ func (a *API) ActivitySkuUpdateBatch(activityID int64, actSkuInfoList []*Activit
"act_sku_info": actSkuInfoList,
})
if err == nil {
- return strings.Split(result.Data.(string), ";"), nil
+ return strings.Split(utils.Interface2String(result.Data), ";"), nil
}
return nil, err
}
diff --git a/platformapi/ebaiapi/callback.go b/platformapi/ebaiapi/callback.go
index 497400f2..c7be08be 100644
--- a/platformapi/ebaiapi/callback.go
+++ b/platformapi/ebaiapi/callback.go
@@ -113,16 +113,12 @@ func (a *API) unmarshalData(cmd string, data []byte, msg interface{}) (callbackR
return nil
}
-func (a *API) CheckCallbackValidation(request *http.Request) (callbackResponse *CallbackResponse) {
- params := make(url.Values)
- for k, v := range request.PostForm {
- params[k] = v
- }
+func (a *API) CheckCallbackValidation(cmd string, params url.Values) (callbackResponse *CallbackResponse) {
sign := a.signParams(params)
- if sign != request.FormValue(signKey) {
- msg := fmt.Sprintf("Signature is not ok, mine:%v, get:%v", sign, request.FormValue(signKey))
+ if sign != params.Get(signKey) {
+ msg := fmt.Sprintf("Signature is not ok, mine:%v, get:%v", sign, params.Get(signKey))
baseapi.SugarLogger.Info(msg)
- return a.Err2CallbackResponse(GetCmd(request), errors.New(msg), nil)
+ return a.Err2CallbackResponse(cmd, errors.New(msg), nil)
}
return nil
}
@@ -130,15 +126,20 @@ func (a *API) CheckCallbackValidation(request *http.Request) (callbackResponse *
func (a *API) GetCallbackMsg(request *http.Request) (msg *CallbackMsg, callbackResponse *CallbackResponse) {
err := request.ParseForm()
if err == nil {
- if callbackResponse = a.CheckCallbackValidation(request); callbackResponse != nil {
- return nil, callbackResponse
+ params := make(url.Values)
+ for k := range request.PostForm {
+ decodedValue, _ := url.QueryUnescape(request.PostFormValue(k))
+ params.Set(k, decodedValue)
}
msg = new(CallbackMsg)
- if callbackResponse = a.unmarshalData(GetCmd(request), []byte(request.FormValue("body")), &msg.Body); callbackResponse != nil {
+ msg.Cmd = GetCmd(request)
+ if callbackResponse = a.CheckCallbackValidation(msg.Cmd, params); callbackResponse != nil {
return nil, callbackResponse
}
- msg.Cmd = GetCmd(request)
- msg.Timestamp = utils.Str2Int64(utils.Interface2String(request.FormValue("timestamp")))
+ if callbackResponse = a.unmarshalData(msg.Cmd, []byte(params.Get("body")), &msg.Body); callbackResponse != nil {
+ return nil, callbackResponse
+ }
+ msg.Timestamp = utils.Str2Int64(utils.Interface2String(params.Get("timestamp")))
var tmpObj interface{}
switch msg.Cmd {
case CmdOrderPartRefund:
@@ -158,6 +159,7 @@ func (a *API) GetCallbackMsg(request *http.Request) (msg *CallbackMsg, callbackR
return nil, a.Err2CallbackResponse("", err, nil)
}
-func GetCmd(request *http.Request) string {
- return request.FormValue("cmd")
+func GetCmd(request *http.Request) (cmd string) {
+ cmd, _ = url.QueryUnescape(request.FormValue("cmd"))
+ return cmd
}
diff --git a/platformapi/ebaiapi/common.go b/platformapi/ebaiapi/common.go
index 49de80ec..4bdf8573 100644
--- a/platformapi/ebaiapi/common.go
+++ b/platformapi/ebaiapi/common.go
@@ -15,18 +15,7 @@ type CityInfo struct {
func (a *API) CommonShopCities(parentID int) (cityList []*CityInfo, err error) {
result, err := a.AccessAPI("common.shopcities", utils.Params2Map("pid", parentID))
if err == nil {
- cityMapList := utils.Slice2MapSlice(result.Data.([]interface{}))
- // baseapi.SugarLogger.Debug(utils.Format4Output(cityMapList, false))
- cityList = make([]*CityInfo, len(cityMapList))
- for k, v := range cityMapList {
- cityList[k] = &CityInfo{
- ID: int(utils.Str2Int64(utils.Interface2String(v["city_id"]))),
- Name: utils.Interface2String(v["city_name"]),
- ParentID: int(utils.Str2Int64(utils.Interface2String(v["parent_id"]))),
- IsOpen: int(utils.Str2Int64(utils.Interface2String(v["is_open"]))),
- }
- }
- return cityList, nil
+ err = utils.Map2StructByJson(result.Data, &cityList, true)
}
- return nil, err
+ return cityList, err
}
diff --git a/platformapi/ebaiapi/ebaiapi.go b/platformapi/ebaiapi/ebaiapi.go
index b3b5f50b..78bdfe8a 100644
--- a/platformapi/ebaiapi/ebaiapi.go
+++ b/platformapi/ebaiapi/ebaiapi.go
@@ -7,7 +7,6 @@ import (
"net/url"
"sort"
"strings"
- "sync"
"git.rosy.net.cn/baseapi"
"git.rosy.net.cn/baseapi/platformapi"
@@ -43,9 +42,6 @@ type API struct {
config *platformapi.APIConfig
speedLimiter *platformapi.Limiter
supplierID int64
-
- locker sync.RWMutex
- storeCookies map[string]string
}
func New(source, secret string, config ...*platformapi.APIConfig) *API {
@@ -60,7 +56,6 @@ func New(source, secret string, config ...*platformapi.APIConfig) *API {
client: &http.Client{Timeout: curConfig.ClientTimeout},
config: &curConfig,
speedLimiter: platformapi.New(apiLimitConfigs, nil), //defaultAPILimitConfig),
- storeCookies: make(map[string]string),
supplierID: -1,
}
@@ -155,12 +150,12 @@ func (a *API) AccessAPI(cmd string, body map[string]interface{}) (retVal *Respon
}
func (a *API) GetSupplierID() (supplierID int64) {
- a.locker.RLock()
+ a.RLock()
supplierID = a.supplierID
- a.locker.RUnlock()
+ a.RUnlock()
if supplierID < 0 {
- a.locker.Lock()
- defer a.locker.Unlock()
+ a.Lock()
+ defer a.Unlock()
a.supplierID = 0
if shopList, err := a.ShopList(SysStatusAll); err == nil {
diff --git a/platformapi/ebaiapi/order.go b/platformapi/ebaiapi/order.go
index bd596118..bfd410e6 100644
--- a/platformapi/ebaiapi/order.go
+++ b/platformapi/ebaiapi/order.go
@@ -41,6 +41,7 @@ const (
WaybillStatusSelfDelivery = "18" // 自行配送
WaybillStatusDontDeliver = "19" // 不再配送
WaybillStatusDeliveryRejected = "20" // 配送拒单
+ WaybillStatusCourierArrived = "21" // 骑士到店
)
const (
@@ -66,8 +67,6 @@ const (
OrderUserCancelTypeBeforeSale = 1 // 表示订单完成前用户全单取消申请流程
OrderUserCancelTypeAfterSale = 2 // 表示订单完成后用户全单退款申请流程
-
- SendImmediatelySelf = 6 // 饿百商家自送的配送状态
)
const (
@@ -188,31 +187,31 @@ type OrderDetailInfo struct {
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"`
+ 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 {
@@ -223,8 +222,8 @@ type OrderDetailInfo struct {
Source string `json:"source"`
User *struct {
Address string `json:"address"`
- City string `json:"city"`
- Coord *struct {
+ // City string `json:"city"`
+ Coord *struct {
Latitude string `json:"latitude"`
Longitude string `json:"longitude"`
} `json:"coord"`
@@ -232,13 +231,13 @@ type OrderDetailInfo struct {
Latitude string `json:"latitude"`
Longitude string `json:"longitude"`
} `json:"coord_amap"`
- District string `json:"district"`
+ // 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"`
+ // Province string `json:"province"`
+ UserID string `json:"user_id"`
} `json:"user"`
}
diff --git a/platformapi/ebaiapi/shop.go b/platformapi/ebaiapi/shop.go
index d4a8e4e6..7f2249d2 100644
--- a/platformapi/ebaiapi/shop.go
+++ b/platformapi/ebaiapi/shop.go
@@ -68,6 +68,18 @@ const (
DeliveryTypeElmXingHuoZBKA = 18 // 星火众包KA
)
+// https://open-be.ele.me/dev/api/doc/v3/#api-Order_Up-order_get
+const (
+ DeliveryPartyFengNiao = 1 // 蜂鸟
+ DeliveryPartyFengNiaoSelf = 2 // 蜂鸟自配送
+ DeliveryPartyFengNiaoZB = 3 // 蜂鸟众包
+ DeliveryPartyFengElmZB = 4 // 饿了么众包
+ DeliveryPartyFengNiaoPS = 5 // 蜂鸟配送
+ DeliveryPartyFengElmSelf = 6 // 饿了么自配送,商家自送?
+ DeliveryPartyFullCity = 7 // 全城送
+ DeliveryPartyKuaiDiPS = 8 // 快递配送
+)
+
type ShopInfo struct {
ShopID string `json:"shop_id"`
BaiduShopID int64 `json:"baidu_shop_id"`
diff --git a/platformapi/ebaiapi/shop_sku.go b/platformapi/ebaiapi/shop_sku.go
index fd282197..f4caaf28 100644
--- a/platformapi/ebaiapi/shop_sku.go
+++ b/platformapi/ebaiapi/shop_sku.go
@@ -237,7 +237,7 @@ func (a *API) ShopCategoryCreate(shopID string, parentID int64, name string, ran
func (a *API) ShopCategoryGet(shopID string) (cats []*CategoryInfo, err error) {
result, err := a.AccessAPI("sku.shop.category.get", utils.Params2Map(KeyShopID, shopID))
if err == nil {
- if inMap, ok := result.Data.(map[string]interface{}); ok { // fuck it
+ if inMap, ok := result.Data.(map[string]interface{}); ok {
cats := interface2CatList(inMap["categorys"], 1)
return cats, nil
}
@@ -252,11 +252,6 @@ func (a *API) ShopCategoryUpdate(shopID string, categoryID int64, name string, r
"name": name,
"rank": rank,
})
- if errWithCode, ok := err.(*utils.ErrorWithCode); ok {
- if errWithCode.Level() == 0 && errWithCode.IntCode() == 1 { //忽略同名错误
- err = nil
- }
- }
return err
}
@@ -367,8 +362,8 @@ func handleShopSkuBatchErr(err error) (opResult *BatchOpResult, outErr error) {
var data interface{}
if err2 := utils.UnmarshalUseNumber([]byte(errExt.ErrMsg()), &data); err2 == nil {
if err2 = utils.Map2StructByJson(data, &opResult, true); err2 == nil {
- // 将以\u表示的字符串标准化
- errExt.SetErrMsg(string(utils.MustMarshal(opResult)))
+ // 将以\u表示的字符串标准化,并去掉成功的
+ errExt.SetErrMsg(string(utils.MustMarshal(opResult.FailedList)))
outErr = errExt
}
}
diff --git a/platformapi/ebaiapi/shop_sku_test.go b/platformapi/ebaiapi/shop_sku_test.go
index d55f5e58..c9649afa 100644
--- a/platformapi/ebaiapi/shop_sku_test.go
+++ b/platformapi/ebaiapi/shop_sku_test.go
@@ -16,7 +16,7 @@ func TestShopCategoryCreate(t *testing.T) {
}
func TestShopCategoryGet(t *testing.T) {
- result, err := api.ShopCategoryGet("102493")
+ result, err := api.ShopCategoryGet("300034")
if err != nil {
t.Fatal(err)
} else {
@@ -69,7 +69,7 @@ func TestSkuUploadRTF(t *testing.T) {
}
func TestSkuCreate(t *testing.T) {
- result, err := api.SkuCreate(testShopID, 17, map[string]interface{}{
+ result, err := api.SkuCreate("", testShopID, 17, map[string]interface{}{
"name": "测试商品",
"status": SkuStatusOnline,
"left_num": MaxLeftNum,
diff --git a/platformapi/ebaiapi/store_page.go b/platformapi/ebaiapi/store_page.go
index e3ddfa4f..c8349d90 100644
--- a/platformapi/ebaiapi/store_page.go
+++ b/platformapi/ebaiapi/store_page.go
@@ -197,7 +197,7 @@ type PageListInnerShopInfo struct {
ShopLabels string `json:"shop_labels"`
ShopName string `json:"shop_name"`
TakeoutCost float64 `json:"takeout_cost"`
- TakeoutPrice int `json:"takeout_price"`
+ TakeoutPrice float64 `json:"takeout_price"`
Type interface{} `json:"type"`
WelfareInfo []struct {
IconColor struct {
@@ -263,39 +263,44 @@ type PageShopInfo struct {
Tag []interface{} `json:"tag"`
Text string `json:"text"`
} `json:"delivery_mode"`
- Description string `json:"description"`
- DisplayRefundLabel int `json:"display_refund_label"`
- Distance int `json:"distance"`
- EleBusinessState int `json:"ele_business_state"`
- EleID string `json:"ele_id"`
- FirstOpenTime *BussinessTimeInfo `json:"first_open_time"`
- HitGod int `json:"hit_god"`
- ImagePath string `json:"image_path"`
- IsColdChain int `json:"is_cold_chain"`
- IsDoubleTwelve int `json:"is_double_twelve"`
- IsMedicineShop int `json:"is_medicine_shop"`
- IsOwnTheme int `json:"is_own_theme"`
- Latitude float64 `json:"latitude"`
- Longitude float64 `json:"longitude"`
- MedicineQualification []interface{} `json:"medicine_qualification"`
- Name string `json:"name"`
- NewStyle bool `json:"new_style"`
- OTakoutCost int `json:"o_takout_cost"`
- OTakoutPrice int `json:"o_takout_price"`
- OrderLeadTime interface{} `json:"order_lead_time"`
- Phone string `json:"phone"`
- PromotionInfo string `json:"promotion_info"`
- Qualification string `json:"qualification"`
- RecentOrderNum int `json:"recent_order_num"`
- ShopID string `json:"shop_id"`
- ShopScore float64 `json:"shop_score"`
- ShopSourceFrom int `json:"shop_source_from"`
- SkuCount int `json:"sku_count"`
- TakeoutCost int `json:"takeout_cost"`
- TakeoutInvoice int `json:"takeout_invoice"`
- TakeoutInvoiceMinPrice string `json:"takeout_invoice_min_price"`
- TakeoutOpenTime string `json:"takeout_open_time"`
- TakeoutPrice int `json:"takeout_price"`
+ Description string `json:"description"`
+ DisplayRefundLabel int `json:"display_refund_label"`
+ Distance int `json:"distance"`
+ EleBusinessState int `json:"ele_business_state"`
+ EleID string `json:"ele_id"`
+ FirstOpenTime *BussinessTimeInfo `json:"first_open_time"`
+ HitGod int `json:"hit_god"`
+ ImagePath string `json:"image_path"`
+ IsColdChain int `json:"is_cold_chain"`
+ IsDoubleTwelve int `json:"is_double_twelve"`
+ IsMedicineShop int `json:"is_medicine_shop"`
+ IsOwnTheme int `json:"is_own_theme"`
+ Latitude float64 `json:"latitude"`
+ Longitude float64 `json:"longitude"`
+ MedicineQualification []*struct {
+ AptitudeName string `json:"aptitude_name"`
+ AptitudePhoto []string `json:"aptitude_photo"`
+ AptitudeType string `json:"aptitude_type"`
+ LicenseNumber string `json:"license_number"`
+ } `json:"medicine_qualification"`
+ Name string `json:"name"`
+ NewStyle bool `json:"new_style"`
+ OTakoutCost int `json:"o_takout_cost"`
+ OTakoutPrice int `json:"o_takout_price"`
+ OrderLeadTime interface{} `json:"order_lead_time"`
+ Phone string `json:"phone"`
+ PromotionInfo string `json:"promotion_info"`
+ Qualification string `json:"qualification"`
+ RecentOrderNum int `json:"recent_order_num"`
+ ShopID string `json:"shop_id"`
+ ShopScore float64 `json:"shop_score"`
+ ShopSourceFrom int `json:"shop_source_from"`
+ SkuCount int `json:"sku_count"`
+ TakeoutCost float64 `json:"takeout_cost"`
+ TakeoutInvoice float64 `json:"takeout_invoice"`
+ TakeoutInvoiceMinPrice string `json:"takeout_invoice_min_price"`
+ TakeoutOpenTime string `json:"takeout_open_time"`
+ TakeoutPrice float64 `json:"takeout_price"`
}
func (a *API) AccessStorePage2(subURL string, params map[string]interface{}, isPost bool, cookies map[string]string) (retVal map[string]interface{}, err error) {
@@ -515,22 +520,45 @@ func (a *API) PageGetCustomCatList(baiduShopID int64) (catList []map[string]inte
return nil, err
}
-func (a *API) GetStoreList(lng string, lat string) (retVal map[string]interface{}, err error) {
+func (a *API) GetStoreList(lng string, lat string, pageNo, pageSize int) (shopListInfo *PageListInfo, err error) {
+ if pageNo <= 0 {
+ pageNo = 1
+ }
+ if pageSize > 20 || pageSize <= 0 {
+ pageSize = 20
+ }
params := map[string]interface{}{
"channel": "kitchen",
- "pn": 1,
- "rn": 999,
+ "rn": pageSize,
+ "pn": pageNo,
"lng": lng,
"lat": lat,
}
- retVal, err = a.AccessStorePageNoCookie("/newretail/main/shoplist", params)
- return retVal, err
+ result, err := a.AccessStorePageNoCookie("newretail/main/shoplist", params)
+ if err == nil && result != nil {
+ err = utils.Map2StructByJson(result, &shopListInfo, true)
+ }
+ return shopListInfo, err
}
func (a *API) GetStoreList2(lng float64, lat float64) (shopListInfo *PageListInfo, err error) {
- retVal, err := a.GetStoreList(fmt.Sprintf("%.6f", lng), fmt.Sprintf("%.6f", lat))
- if err == nil {
- err = utils.Map2StructByJson(retVal, &shopListInfo, true)
+ pageSize := 20
+ pageNo := 1
+ for {
+ retVal, err2 := a.GetStoreList(fmt.Sprintf("%.6f", lng), fmt.Sprintf("%.6f", lat), pageNo, pageSize)
+ if err = err2; err == nil {
+ if shopListInfo == nil {
+ shopListInfo = retVal
+ } else {
+ shopListInfo.ShopList = append(shopListInfo.ShopList, retVal.ShopList...)
+ }
+ if len(retVal.ShopList) < pageSize {
+ break
+ }
+ pageNo++
+ } else {
+ break
+ }
}
return shopListInfo, err
}
diff --git a/platformapi/ebaiapi/store_page_test.go b/platformapi/ebaiapi/store_page_test.go
index ca6bc4b4..7537fe8f 100644
--- a/platformapi/ebaiapi/store_page_test.go
+++ b/platformapi/ebaiapi/store_page_test.go
@@ -107,7 +107,7 @@ func TestGetShopHealthByDetail(t *testing.T) {
}
func TestGetStoreList(t *testing.T) {
- result, err := api.GetStoreList("104.057218", "30.6949")
+ result, err := api.GetStoreList("104.057218", "30.6949", 0, 0)
if err != nil {
t.Fatal(err)
}
@@ -115,7 +115,7 @@ func TestGetStoreList(t *testing.T) {
}
func TestGetStoreList2(t *testing.T) {
- result, err := api.GetStoreList2(120.074911, 29.306863)
+ result, err := api.GetStoreList2(104.010554, 30.637072)
if err != nil {
t.Fatal(err)
}
diff --git a/platformapi/jdapi/jdapi.go b/platformapi/jdapi/jdapi.go
index 7c1b5e64..2c78e6c8 100644
--- a/platformapi/jdapi/jdapi.go
+++ b/platformapi/jdapi/jdapi.go
@@ -1,13 +1,13 @@
package jdapi
import (
+ "bytes"
"crypto/md5"
"encoding/json"
"fmt"
"net/http"
"sort"
"strings"
- "sync"
"git.rosy.net.cn/baseapi"
@@ -66,9 +66,6 @@ type API struct {
appSecret string
client *http.Client
config *platformapi.APIConfig
-
- locker sync.RWMutex
- storeCookie string
}
var (
@@ -105,7 +102,8 @@ var (
KeyOutStationNo,
KeyStationNo,
"StoreNo",
- "outSkuId",
+ KeyOutSkuId,
+ KeySkuId,
}
)
@@ -120,13 +118,14 @@ func (a *API) signParams(jdParams map[string]interface{}) string {
}
sort.Strings(keys)
- allStr := a.appSecret
+ buf := &bytes.Buffer{}
+ buf.WriteString(a.appSecret)
for _, k := range keys {
- allStr += k + fmt.Sprint(jdParams[k])
+ buf.WriteString(k)
+ buf.WriteString(fmt.Sprint(jdParams[k]))
}
- allStr = allStr + a.appSecret
-
- return fmt.Sprintf("%X", md5.Sum([]byte(allStr)))
+ buf.WriteString(a.appSecret)
+ return fmt.Sprintf("%X", md5.Sum(buf.Bytes()))
}
func New(token, appKey, appSecret string, config ...*platformapi.APIConfig) *API {
@@ -143,6 +142,13 @@ func New(token, appKey, appSecret string, config ...*platformapi.APIConfig) *API
}
}
+func NewPageOnly(cookie string, config ...*platformapi.APIConfig) *API {
+ api := New("", "", "", config...)
+ api.SetCookie(accessStorePageCookieName, cookie)
+ api.SetCookie(accessStorePageCookieName2, cookie)
+ return api
+}
+
func (a *API) AccessAPI2(apiStr string, jdParams map[string]interface{}, traceInfo string) (retVal map[string]interface{}, err error) {
params := make(map[string]interface{})
params["v"] = "1.0"
@@ -267,59 +273,60 @@ func (a *API) AccessAPINoPage2(apiStr string, jdParams map[string]interface{}, k
func (a *API) AccessAPINoPage(apiStr string, jdParams map[string]interface{}, keyToRemove, keyToKeep []string, resultParser func(data map[string]interface{}) (interface{}, error)) (interface{}, error) {
return a.AccessAPINoPage2(apiStr, jdParams, keyToRemove, keyToKeep, resultParser, "")
}
+func genNormalHavePageResultParser(dataKey string) (handler PageResultParser) {
+ return func(data map[string]interface{}, totalCount int) ([]interface{}, int, error) {
+ var result map[string]interface{}
+ var retVal []interface{}
-func normalHavePageResultParser(data map[string]interface{}, totalCount int) ([]interface{}, int, error) {
- var result map[string]interface{}
- var retVal []interface{}
-
- tempResult := data["result"]
- if resultStr, ok := tempResult.(string); ok {
- if err := utils.UnmarshalUseNumber([]byte(resultStr), &tempResult); err != nil {
- return nil, 0, platformapi.ErrResponseDataFormatWrong
- }
- }
-
- result = tempResult.(map[string]interface{})
-
- if totalCount == 0 {
- for _, totalCountKey := range havePageTotalCountKeys {
- if totalCount2, ok := result[totalCountKey]; ok {
- totalCountInt64, _ := totalCount2.(json.Number).Int64()
- totalCount = int(totalCountInt64)
- if totalCount == 0 {
- return make([]interface{}, 0), 0, nil
- }
- break
+ tempResult := data[dataKey]
+ if resultStr, ok := tempResult.(string); ok {
+ if err := utils.UnmarshalUseNumber([]byte(resultStr), &tempResult); err != nil {
+ return nil, 0, platformapi.ErrResponseDataFormatWrong
}
}
+
+ result = tempResult.(map[string]interface{})
+
if totalCount == 0 {
- baseapi.SugarLogger.Errorf("can not find totalCount key, data:%v", result)
- return nil, 0, platformapi.ErrResponseDataFormatWrong
- }
- }
-
- for _, inner2ResultKey := range havePageInner2DataKeys {
- if inner2Result, ok := result[inner2ResultKey]; ok {
- if inner2Result == nil {
- retVal = nil
- } else if inner2ResultStr, ok := inner2Result.(string); ok {
- err := utils.UnmarshalUseNumber([]byte(inner2ResultStr), &retVal)
- if err != nil {
- return nil, 0, platformapi.ErrResponseDataFormatWrong
+ for _, totalCountKey := range havePageTotalCountKeys {
+ if totalCount2, ok := result[totalCountKey]; ok {
+ totalCountInt64, _ := totalCount2.(json.Number).Int64()
+ totalCount = int(totalCountInt64)
+ if totalCount == 0 {
+ return make([]interface{}, 0), 0, nil
+ }
+ break
}
- } else {
- retVal = inner2Result.([]interface{})
}
- return retVal, totalCount, nil
+ if totalCount == 0 {
+ baseapi.SugarLogger.Errorf("can not find totalCount key, data:%v", result)
+ return nil, 0, platformapi.ErrResponseDataFormatWrong
+ }
}
+
+ for _, inner2ResultKey := range havePageInner2DataKeys {
+ if inner2Result, ok := result[inner2ResultKey]; ok {
+ if inner2Result == nil {
+ retVal = nil
+ } else if inner2ResultStr, ok := inner2Result.(string); ok {
+ err := utils.UnmarshalUseNumber([]byte(inner2ResultStr), &retVal)
+ if err != nil {
+ return nil, 0, platformapi.ErrResponseDataFormatWrong
+ }
+ } else {
+ retVal = inner2Result.([]interface{})
+ }
+ return retVal, totalCount, nil
+ }
+ }
+ baseapi.SugarLogger.Errorf("can not find result key, data:%v", result)
+ return nil, 0, platformapi.ErrResponseDataFormatWrong
}
- baseapi.SugarLogger.Errorf("can not find result key, data:%v", result)
- return nil, 0, platformapi.ErrResponseDataFormatWrong
}
func (a *API) AccessAPIHavePage(apiStr string, jdParams map[string]interface{}, keyToRemove, keyToKeep []string, pageResultParser PageResultParser) ([]interface{}, int, error) {
if pageResultParser == nil {
- pageResultParser = normalHavePageResultParser
+ pageResultParser = genNormalHavePageResultParser("result")
}
localJdParams := make(map[string]interface{})
diff --git a/platformapi/jdapi/jdapi_test.go b/platformapi/jdapi/jdapi_test.go
index b6bd0372..957260c2 100644
--- a/platformapi/jdapi/jdapi_test.go
+++ b/platformapi/jdapi/jdapi_test.go
@@ -23,11 +23,11 @@ func init() {
// sandbox
api = New("df97f334-f7d8-4b36-9664-5784d8ae0baf", "06692746f7224695ad4788ce340bc854", "d6b42a35a7414a5490d811654d745c84")
// prod
- // api = New("ccb10daf-e6f5-4a58-ada5-b97f9073a137", "1dba76d40cac446ca500c0391a0b6c9d", "a88d031a1e7b462cb1579f12e97fe7f4")
+ // api = New("77e703b7-7997-441b-a12a-2e522efb117a", "1dba76d40cac446ca500c0391a0b6c9d", "a88d031a1e7b462cb1579f12e97fe7f4")
// 天天果园
// api = New("84541069-fbe2-424b-b625-9b2ba1d4c9e6", "5d5577a2506f41b8b4ec520ba83490f5", "0b01b9eeb15b41dab1c3d05d95c17a26")
- const cookieValue = "YYJV3NHVBPHLD36FWP6F3EM5PTXJ2XZQS7U4HWRIDPP4IWGUKUIB4XG5N26CZRDLDF7PKOXBPD6BNTUAJLETLZOIWMCVFI3K6MYZIY4QBIXIMXYDJNUKFGJVQTN5356SAD6WPCIHWNQAG7DDMF7L7S3SHDYZP7PPVMRGO4VWG2JRBMKFTOGIWZ5L2XHXC3SXQ4OLX7EL4RKUPZQT6GOH63KE3EVK37L5LG7TGSDGXFQP4377YK72UB5YZG6IJH6PY25YLLCJYPMDSHKPGYBUFJ4MMMKGN6MWB37CP7XVDBBZJ3U462ENTEXH744AWCQCIG2AAE2PKYVHC"
+ const cookieValue = "YYJV3NHVBPHLD36FWP6F3EM5PTXJ2XZQS7U4HWRIDPP4IWGUKUIB4XG5N26CZRDLDF7PKOXBPD6BNTUAJLETLZOIWMCVFI3K6MYZIY4QBIXIMXYDJNUKFGJVQTN5356SAD6WPCIHWNQAG7DDMF7L7S3SHCT3RM3CQG7IJIPUQ3THS5UIUYWMKINM7ETUOQB7OBPOPZVCT3ZJY55243TDVXLO25PP4UYSPTTPMNQ7HPMWOJKJ3BJWGVHD243MXH7NZWW264TKN5UOCJBSSSOKD2QQII"
api.SetCookie(accessStorePageCookieName, cookieValue)
api.SetCookie(accessStorePageCookieName2, cookieValue)
}
diff --git a/platformapi/jdapi/order.go b/platformapi/jdapi/order.go
index ee15e905..f1b62f5b 100644
--- a/platformapi/jdapi/order.go
+++ b/platformapi/jdapi/order.go
@@ -37,11 +37,10 @@ const (
)
const (
- FreightDiscountTypeByShop = 8 // 8:商家满免运费
- FreightDiscountTypeByVip = 7 // 7:VIP免运费
- FreightDiscountTypeByActivity = 12 // 12:首单地推满免运费
- FreightDiscountTypeByCoupons = 15 // 15:运费券
- SelfDeliveryCarrierNo = "2938" // 京东配送方式=商家自送
+ FreightDiscountTypeByShop = 8 // 8:商家满免运费
+ FreightDiscountTypeByVip = 7 // 7:VIP免运费
+ FreightDiscountTypeByActivity = 12 // 12:首单地推满免运费
+ FreightDiscountTypeByCoupons = 15 // 15:运费券
)
const (
@@ -59,16 +58,17 @@ const (
)
const (
- AfsReasonTypeGoodsQuality = 201 // 商品质量问题/做工粗糙/有瑕疵
- AfsReasonTypeWrongGoods = 202 // 发错货
- AfsReasonTypeMissingGoods = 203 // 部分商品未收到
- AfsReasonTypeNoGoods = 501 // 全部商品未收到
- AfsReasonTypeDamagedGoods = 208 // 外表损伤(压坏,磕坏等)
- AfsReasonTypeGoodsQuantity = 207 // 缺斤少两
- AfsReasonTypeAgreedByMerchant = 209 // 与商家协商一致
+ AfsReasonTypeGoodsQuality = 201 // 商品质量问题
+ AfsReasonTypeWrongGoods = 202 // 送错货
+ AfsReasonTypeMissingGoods = 203 // 缺件少件
+ AfsReasonTypeNoGoods = 501 // 全部商品未收到
+ AfsReasonTypeDamagedGoods = 208 // 包装脏污有破损
+ AfsReasonTypeGoodsQuantity = 207 // 缺斤少两
+ // AfsReasonTypeAgreedByMerchant = 209 // 与商家协商一致,2019/09/01取消
+ AfsReasonTypeGoodsAbsent = 210 // 商家通知我缺货
AfsReasonTypeGoodsSizeNoSame = 302 // 大小尺寸与商品描述不符
- AfsReasonTypeGoodsColorNoSame = 303 // 颜色/款式/图案与描述不符
- AfsReasonWrongPurchase = 402 // 误购
+ AfsReasonTypeGoodsColorNoSame = 303 // 实物与原图不符
+ AfsReasonWrongPurchase = 402 // 不想要了
AfsReasonNotReceivedIntime = 502 // 未在时效内送达
)
@@ -310,6 +310,48 @@ type OrderInfo struct {
Yn bool `json:"yn"`
}
+type OrderQueryParam struct {
+ PageNo int64 `json:"pageNo,omitempty"` // 当前页数,默认:1
+ PageSize int `json:"pageSize,omitempty"` // 每页条数,默认:20,最大值100
+ OrderID int64 `json:"orderId,omitempty"`
+
+ OrderPurchaseTimeBegin string `json:"orderPurchaseTime_begin,omitempty"` // 购买成交时间-支付(开始)
+ OrderPurchaseTimeEnd string `json:"orderPurchaseTime_end,omitempty"` // 购买成交时间-支付(结束)
+ OrderStatus int `json:"orderStatus,omitempty"`
+
+ DeliveryStationNo string `json:"deliveryStationNo,omitempty"` // 到家门店编码
+ DeliveryStationNoIsv string `json:"deliveryStationNoIsv,omitempty"` // 商家门店编码
+}
+
+type SonTag struct {
+ MqProcessTime string `json:"mqProcessTime"`
+ OperTime string `json:"operTime"`
+ TagCode int `json:"tagCode"`
+ CodeName string `json:"codeName"`
+ OperPin string `json:"operPin"`
+ OperName int `json:"operName"`
+ MsgContent string `json:"msgContent"`
+}
+
+type OrderTrack struct {
+ SonTagList []*SonTag `json:"sonTagList"`
+ IsThirdCarry int `json:"isThirdCarry"`
+ OperFrom string `json:"operFrom"`
+ MqProcessTime string `json:"mqProcessTime"`
+ TagIcon string `json:"tagIcon"`
+ OperTime string `json:"operTime"`
+ ThirdCarry int `json:"thirdCarry"`
+ TagTitle string `json:"tagTitle"`
+ CodeName string `json:"codeName"`
+ TagCode int `json:"tagCode"`
+ OperPin string `json:"operPin"`
+ OperName string `json:"operName"`
+ Care bool `json:"care"`
+ MsgContent string `json:"msgContent"`
+ MsgContentApp string `json:"msgContentApp"`
+ DoNotCareReason string `json:"doNotCareReason"`
+}
+
var (
ErrCanNotFindOrder = errors.New("can not find order")
)
@@ -336,14 +378,6 @@ func (a *API) OrderQuery(jdParams map[string]interface{}) (retVal []interface{},
return retVal, totalCount, err
}
-func (a *API) OrderQuery2(jdParams map[string]interface{}) (retVal []*OrderInfo, totalCount int, err error) {
- orderList, totalCount, err := a.OrderQuery(jdParams)
- if err == nil {
- err = utils.Map2StructByJson(orderList, &retVal, true)
- }
- return retVal, totalCount, err
-}
-
// orderFreightMoney 基础运费
// tips 商家承担小费
// merchantPaymentDistanceFreightMoney 取件服务费(开票)(正向单展示远距离运费;售后单则展示达达售后运费)
@@ -361,6 +395,28 @@ func (a *API) QuerySingleOrder(orderId string) (map[string]interface{}, error) {
return result[0].(map[string]interface{}), nil
}
+func (a *API) OrderQuery2(queryParam *OrderQueryParam) (retVal []*OrderInfo, totalCount int, err error) {
+ orderList, totalCount, err := a.OrderQuery(utils.Struct2MapByJson(queryParam))
+ if err == nil {
+ err = utils.Map2StructByJson(orderList, &retVal, true)
+ }
+ return retVal, totalCount, err
+}
+
+func (a *API) QuerySingleOrder2(orderID string) (orderInfo *OrderInfo, err error) {
+ orderList, _, err := a.OrderQuery2(&OrderQueryParam{
+ OrderID: utils.Str2Int64(orderID),
+ })
+ if err == nil {
+ if len(orderList) > 0 {
+ orderInfo = orderList[0]
+ } else {
+ err = ErrCanNotFindOrder
+ }
+ }
+ return orderInfo, err
+}
+
// 商家确认接单接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=169&apiid=c1a15129d1374e9da7fa35487f878604
func (a *API) OrderAcceptOperate(orderId string, isAgreed bool, userName string) error {
@@ -588,6 +644,7 @@ func (a *API) AfsSubmit(OrderID, pin, questionTypeCode, questionDesc, questionPi
"orderId": OrderID,
"pin": utils.GetAPIOperator(pin),
"questionTypeCode": questionTypeCode,
+ "skuList": skuList,
}
if questionDesc != "" {
jdParams["questionDesc"] = questionDesc
@@ -624,13 +681,53 @@ func ProcessQuestionPic(questionPic string) (outQuestionPic string) {
}
// 订单商家加小费接口
+// tips必须是100的倍数,另外必须要等到家下发运单后才能调用,否则会报“订单号srcOrderNo的订单不存在”错
+// 这个tips是累计增的,比如第一次调用tips为100,再一次调用tips为200,则总共的tips就是300
// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=169&apiid=ed9e3ca7325c4d4d8ceaf959ed0e7a62
-func (a *API) OrderAddTips(OrderID string, tips int, pin string) (err error) {
+func (a *API) OrderAddTips(orderID string, tips int, operator string) (err error) {
jdParams := map[string]interface{}{
- "orderId": OrderID,
- "tips": tips,
- "pin": utils.GetAPIOperator(pin),
+ "orderId": orderID,
+ "tips": tips,
+ "operator": utils.GetAPIOperator(operator),
}
_, err = a.AccessAPINoPage("order/addTips", jdParams, nil, nil, nullResultParser)
return err
}
+
+// 根据订单号查询订单跟踪接口
+// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=169&apiid=d9d4fd73fba14fd8851a4c054d2ee42e
+func (a *API) GetByOrderNoForOaos(orderNo string) (orderTrackList []*OrderTrack, err error) {
+ jdParams := map[string]interface{}{
+ "orderNo": orderNo,
+ }
+ result, err := a.AccessAPINoPage("orderTrace/getByOrderNoForOaos", jdParams, nil, nil, genNoPageResultParser("code", "msg", "orderTrackList", "0"))
+ if err == nil {
+ err = utils.Map2StructByJson(result, &orderTrackList, false)
+ }
+ return orderTrackList, err
+}
+
+// 新版根据订单号查询订单跟踪接口
+// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=169&apiid=6450cd91dd5b4dc0bb6a6cd17af6d0a4
+func (a *API) GetByOrderNoForOaosNew(orderID string) (orderTrackList []*OrderTrack, err error) {
+ jdParams := map[string]interface{}{
+ "orderId": orderID,
+ }
+ result, err := a.AccessAPINoPage("orderTrace/getByOrderNoForOaosNew", jdParams, nil, nil, genNoPageResultParser("code", "detail", "result", "0"))
+ if err == nil {
+ err = utils.Map2StructByJson(result.(map[string]interface{})["orderTrackList"], &orderTrackList, false)
+ }
+ return orderTrackList, err
+}
+
+// 订单自提码核验接口
+// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=169&apiid=428fa2cb66784b64a85db36ec2972ff9
+func (a *API) CheckSelfPickCode(selfPickCode, orderID, operPin string) (err error) {
+ jdParams := map[string]interface{}{
+ "selfPickCode": selfPickCode,
+ "orderId": orderID,
+ "operPin": operPin,
+ }
+ _, err = a.AccessAPINoPage("ocs/checkSelfPickCode", jdParams, nil, nil, nullResultParser)
+ return err
+}
diff --git a/platformapi/jdapi/order_test.go b/platformapi/jdapi/order_test.go
index 18e65ff6..b95119c4 100644
--- a/platformapi/jdapi/order_test.go
+++ b/platformapi/jdapi/order_test.go
@@ -10,7 +10,8 @@ import (
)
func TestQuerySingleOrder(t *testing.T) {
- retVal, err := api.QuerySingleOrder("815536199000222")
+ retVal, err := api.QuerySingleOrder("921160248000222")
+ t.Log(utils.Format4Output(retVal, false))
if err != nil {
t.Error(err)
}
@@ -218,3 +219,43 @@ func TestOrderShoudSettlementService2(t *testing.T) {
}
sugarLogger.Debug(utils.Format4Output(result, false))
}
+
+func TestOrderAddTips(t *testing.T) {
+ err := api.OrderAddTips("918092290000042", 50, "xjh")
+ if err != nil {
+ t.Fatal(err.Error())
+ }
+}
+
+func TestOrderQuery2(t *testing.T) {
+ orderList, _, err := api.OrderQuery2(&OrderQueryParam{
+ OrderID: 918092290000042,
+ })
+ t.Log(utils.Format4Output(orderList, false))
+ if err != nil {
+ t.Fatal(err.Error())
+ }
+}
+
+func TestGetByOrderNoForOaos(t *testing.T) {
+ orderList, err := api.GetByOrderNoForOaos("921235438000341")
+ t.Log(utils.Format4Output(orderList, false))
+ if err != nil {
+ t.Fatal(err.Error())
+ }
+}
+
+func TestGetByOrderNoForOaosNew(t *testing.T) {
+ orderList, err := api.GetByOrderNoForOaosNew("921235438000341")
+ t.Log(utils.Format4Output(orderList, false))
+ if err != nil {
+ t.Fatal(err.Error())
+ }
+}
+
+func TestCheckSelfPickCode(t *testing.T) {
+ err := api.CheckSelfPickCode("020606", "921235438000341", "test")
+ if err != nil {
+ t.Fatal(err.Error())
+ }
+}
diff --git a/platformapi/jdapi/promotion_audit.go b/platformapi/jdapi/promotion_audit.go
index e993891f..a0c9d2a2 100644
--- a/platformapi/jdapi/promotion_audit.go
+++ b/platformapi/jdapi/promotion_audit.go
@@ -106,3 +106,18 @@ func (a *API) QueryPromotionInfo(promotionInfoId int64) (promotionInfo *Promotio
}
return promotionInfo, err
}
+
+// 根据到家商品ID查询单品级优惠活动列表接口
+// https://openo2o.jddj.com/staticnew/widgets/resources.html?groupid=196&apiid=d73baba02c484109a3c3c1b1236ca13d
+func (a *API) QueryPromotionSku(promotionType int, skuID int64, promotionState int /*, pageNo int64, pageSize int*/) (skuResultList []*PromotionLspQuerySkuResult, err error) {
+ jdParams := map[string]interface{}{
+ "promotionType": promotionType,
+ "skuId": skuID,
+ "promotionState": promotionState,
+ }
+ result, _, err := a.AccessAPIHavePage("singlePromote/queryPromotionSku", jdParams, nil, nil, genNormalHavePageResultParser("data"))
+ if err != nil {
+ err = utils.Map2StructByJson(result, &skuResultList, false)
+ }
+ return skuResultList, err
+}
diff --git a/platformapi/jdapi/promotion_audit_test.go b/platformapi/jdapi/promotion_audit_test.go
index b1b670f9..70ec955a 100644
--- a/platformapi/jdapi/promotion_audit_test.go
+++ b/platformapi/jdapi/promotion_audit_test.go
@@ -21,3 +21,24 @@ func TestQueryPromotionInfo(t *testing.T) {
}
t.Log(utils.Format4Output(result, false))
}
+
+func TestQueryPromotionSku(t *testing.T) {
+ skuIDs := []int64{
+ 2023335105,
+ // 2023335104,
+ // 2023335088,
+ // 2023335057,
+ // 2023335098,
+ // 2023335020,
+ }
+ for _, skuID := range skuIDs {
+ list, err := api.QueryPromotionSku(PromotionTypeDirectDown, skuID, PromotionStateConfirmed)
+ t.Log(utils.Format4Output(list, false))
+ if err != nil {
+ t.Fatal(err)
+ }
+ // for _, v := range list {
+ // CancelPromotionSkuSingle()
+ // }
+ }
+}
diff --git a/platformapi/jdapi/promotion_sku_test.go b/platformapi/jdapi/promotion_sku_test.go
index de1865b1..44eb1135 100644
--- a/platformapi/jdapi/promotion_sku_test.go
+++ b/platformapi/jdapi/promotion_sku_test.go
@@ -76,3 +76,70 @@ func TestCreatePromotionSkuLimitTime(t *testing.T) {
t.Fatal(err)
}
}
+
+func TestCancelPromotion(t *testing.T) {
+ promotionIDs := []int64{
+ 24636389,
+ 24753178,
+ 24754087,
+ 24970355,
+ 24970383,
+ 24970389,
+ 25439508,
+ 25444342,
+ 25869363,
+ 25871946,
+ 26291079,
+ 27036663,
+ 27407558,
+ 27407649,
+ 27424152,
+ 27424181,
+ 27424247,
+ 27424568,
+ 27508490,
+ 27555133,
+ 27674289,
+ 30969372,
+ 41809699,
+ 41810076,
+ 41810296,
+ 41811111,
+ 44179379,
+ 54080816,
+ 54080829,
+ 54080842,
+ 55346987,
+ 55347123,
+ 55347340,
+ 55348499,
+ 55348706,
+ 55348999,
+ 55349177,
+ 56723852,
+ 56724283,
+ 56725840,
+ 56725955,
+ 56726053,
+ 60713479,
+ 60714768,
+ 60719242,
+ 68818338,
+ }
+
+ for _, v := range promotionIDs {
+ promotionInfo, err := api.QueryPromotionInfo(v)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // t.Log(promotionInfo.PromotionType)
+ if promotionInfo.PromotionType == PromotionTypeLimitedTime {
+ err = api.CancelPromotionLimitTime(v, "", utils.GetUUID())
+ } else {
+ err = api.CancelPromotionSingle(v, "", utils.GetUUID())
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+}
diff --git a/platformapi/jdapi/sku.go b/platformapi/jdapi/sku.go
index 9c5239db..abb122b4 100644
--- a/platformapi/jdapi/sku.go
+++ b/platformapi/jdapi/sku.go
@@ -1,12 +1,10 @@
package jdapi
import (
- "fmt"
"regexp"
"strings"
"git.rosy.net.cn/baseapi/utils"
- "git.rosy.net.cn/jx-callback/globals"
)
const (
@@ -65,12 +63,14 @@ const (
const (
MaxBatchSize4BatchUpdateOutSkuId = 50
+ MaxPageSize4QuerySku = 50
+ MaxSkuIDsCount4QueryListBySkuIds = 25
)
const (
- SkuFixedStatusOnline = 1
- SkuFixedStatusOffline = 2
- SkuFixedStatusDeleted = 4
+ SkuFixedStatusOnline = 1 // 上架
+ SkuFixedStatusOffline = 2 // 下架
+ SkuFixedStatusDeleted = 4 // 删除
)
const (
@@ -79,12 +79,21 @@ const (
CreateSpuResultFailed = 3
)
+const (
+ IsFilterDelTrue = "0" // 0代表不查已删除商品
+)
+
const (
MaxSkuNameCharCount = 45 // skuname最长字符数
SaleAttrIDBase = 1001
SaleAttrValueIDBase = 10
)
+const (
+ ImgTypeMain = 1 // 商品图片
+ ImgTypeDetail = 2 // 详情图片
+)
+
type SkuIDPair struct {
SkuId int64 `json:"skuId"`
OutSkuId string `json:"outSkuId"`
@@ -119,11 +128,11 @@ type CategoryInfo struct {
}
type CreateByUpcParam struct {
- Upc string
- OutSkuId string
- Price int // 单位为分
- ShopCategoryId int64
- IsSale bool
+ UniqueUpc string `json:"uniqueCode"` // 商品UPC码
+ OutSku string `json:"outSku"` // 商家商品编码,商家系统中唯一编码,限1-35字符,与到家商品编码一对一对应
+ JdPrice string `json:"jdPrice"` // 商家商品价格(单位:元),用于初始商品门店价格,所有的商品门店价格都会初始化成该值。后续修改商品门店价格需要通过价格类接口修改。
+ ShopCategoryID int64 `json:"shopCategoryId"` // 商家店内分类编码,店内分类编码通过查询商家店内分类信息接口获取
+ IsSale bool `json:"isSale"` // 门店商品可售状态,true为可售,false为不可售,默认为可售。
}
type CreateByUpcPair struct {
@@ -131,13 +140,58 @@ type CreateByUpcPair struct {
SkuId int64
}
+type QuerySkuParam struct {
+ SkuName string `json:"skuName,omitempty"`
+ UpcCode string `json:"upcCode,omitempty"`
+ SkuID int64 `json:"skuId,omitempty"`
+ IsFilterDel string `json:"isFilterDel,omitempty"` // 是否查询出已删除的上传商品(0代表不查已删除商品,不填则查出全部商品)
+ PageNo int `json:"pageNo,omitempty"`
+ PageSize int `json:"pageSize,omitempty"`
+}
+
+type SkuMain struct {
+ SuperID int64 `json:"superId"`
+ SkuID int64 `json:"skuId"`
+ OutSkuID string `json:"outSkuId"`
+ CategoryID int64 `json:"categoryId"`
+ BrandID int64 `json:"brandId"`
+ ShopCategories []int64 `json:"shopCategories"`
+ SkuName string `json:"skuName"`
+ Slogan string `json:"slogan"`
+ FixedStatus int `json:"fixedStatus"` // 商家商品上下架状态(1:上架;2:下架;4:删除;)
+ FixedUpTime string `json:"fixedUpTime"`
+ OrgCode int `json:"orgCode"`
+ SellCities []int64 `json:"sellCities"`
+ SkuPrice int `json:"skuPrice"`
+ Weight float64 `json:"weight"`
+}
+
+type QueryListBySkuIdsParam struct {
+ SkuIDs []int64 `json:"skuIds"`
+ ImgType int `json:"imgType,omitempty"`
+ HandleStatus []int `json:"handleStatus,omitempty"`
+}
+
+type ImgHandleQueryResult struct {
+ ID string `json:"id"`
+ SkuID int64 `json:"skuId"`
+ IsMain int `json:"isMain"`
+ ImgType int `json:"imgType"`
+ SourceImgURL string `json:"sourceImgUrl"`
+ SkuImgSort int `json:"skuImgSort"`
+ HandleStatus int `json:"handleStatus"`
+ HandleStatusStr string `json:"handleStatusStr"`
+ HandleRemark string `json:"handleRemark"`
+ HandleErrLog string `json:"handleErrLog"`
+}
+
var (
skuExistReg = regexp.MustCompile(`商家skuId已存在:(\d+)`)
)
// 分页查询商品品牌信息接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=180&apiid=1ca07a3e767649a7a44fc6ea7e9ed8dd
-func (a *API) QueryPageBrandInfo(pageNo, pageSize, brandId int, brandName string) (brandList []*BrandInfo, totalCount int, err error) {
+func (a *API) QueryPageBrandInfo(pageNo, pageSize int, brandId int64, brandName string) (brandList []*BrandInfo, totalCount int, err error) {
if pageNo <= 0 {
pageNo = 1
}
@@ -320,44 +374,28 @@ func (a *API) BatchUpdateOutSkuId(skuInfoList []*SkuIDPair) (retVal interface{},
// 查询商家已上传商品信息列表接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=180&apiid=e433b95f74524dab91718432c0358977
// pageNo 从1开始
-func (a *API) QuerySkuInfos(skuName string, skuId int64, pageNo, pageSize int, isFilterDel bool) (retVal []map[string]interface{}, totalCount int, err error) {
- if pageNo <= 0 {
- pageNo = 1
+func (a *API) QuerySkuInfos(queryParam *QuerySkuParam) (skuList []*SkuMain, totalCount int, err error) {
+ if queryParam.PageNo <= 0 {
+ queryParam.PageNo = 1
}
- if pageSize == 0 {
- pageSize = 50
+ if queryParam.PageSize == 0 {
+ queryParam.PageSize = MaxPageSize4QuerySku
}
- params := map[string]interface{}{
- KeyPageNo: pageNo, // pageNo好像必须要有值,否则一直不返回
- KeyPageSize: pageSize,
- }
- if skuName != "" {
- params[KeySkuName] = skuName
- }
- if skuId != 0 {
- params[KeySkuId] = skuId
- }
- if isFilterDel {
- params[KeyIsFilterDel] = "1"
- } else {
- params[KeyIsFilterDel] = "0"
- }
- result, totalCount, err := a.AccessAPIHavePage("pms/querySkuInfos", params, nil, nil, nil)
+ result, totalCount, err := a.AccessAPIHavePage("pms/querySkuInfos", utils.Struct2MapByJson(queryParam), nil, nil, nil)
if err == nil {
- return utils.Slice2MapSlice(result), totalCount, nil
+ err = utils.Map2StructByJson(result, &skuList, false)
}
- return nil, 0, err
+ return skuList, totalCount, err
}
// 查询商品图片处理结果接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=180&apiid=17506653e03542f9a49023711780c30d
-func (a *API) QueryListBySkuIds(skuIds []int64, addParams map[string]interface{}) (retVal []map[string]interface{}, err error) {
- result, err := a.AccessAPINoPage("order/queryListBySkuIds", utils.MergeMaps(addParams, utils.Params2Map("skuIds", skuIds)), nil, nil, nil)
+func (a *API) QueryListBySkuIds(queryParam *QueryListBySkuIdsParam) (imgList []*ImgHandleQueryResult, err error) {
+ result, err := a.AccessAPINoPage("order/queryListBySkuIds", utils.Struct2MapByJson(queryParam), nil, nil, nil)
if err == nil {
- return utils.Slice2MapSlice(result.([]interface{})), nil
+ err = utils.Map2StructByJson(result, &imgList, false)
}
- return nil, err
-
+ return imgList, err
}
// 分页查询京东到家商品前缀库接口
@@ -543,22 +581,12 @@ func (a *API) GetSpuSaleAttr(outSpuId string) (attrList []map[string]interface{}
return nil, err
}
-func (a *API) BatchAddSku(paramList []*CreateByUpcParam) (pairs []*CreateByUpcPair, err error) {
- batchSkuRequestList := make([]map[string]interface{}, len(paramList))
- for k, v := range paramList {
- batchSkuRequestList[k] = map[string]interface{}{
- "uniqueCode": v.Upc,
- "outSku": v.OutSkuId,
- "jdPrice": fmt.Sprintf("%.2f", float32(v.Price)/100),
- "shopCategoryId": v.ShopCategoryId,
- "isSale": v.IsSale,
- }
- }
+func (a *API) BatchAddSku(batchSkuRequestList []*CreateByUpcParam) (pairs []*CreateByUpcPair, err error) {
result, err := a.AccessAPINoPage("PmsSkuMainService/batchAddSku", map[string]interface{}{
"batchSkuRequestList": batchSkuRequestList,
}, nil, nil, genNoPageResultParser("code", "result", "detail", "0"))
if err == nil {
- globals.SugarLogger.Debug(utils.Format4Output(result, false))
+ // globals.SugarLogger.Debug(utils.Format4Output(result, false))
// todo 这个API在找不到UPC创建失败时,code也是0,底层不能判断失败
if result2, ok := result.([]interface{}); ok && len(result2) > 0 {
detail := utils.Slice2MapSlice(result2)
diff --git a/platformapi/jdapi/sku_test.go b/platformapi/jdapi/sku_test.go
index 950dcbbf..b44cb514 100644
--- a/platformapi/jdapi/sku_test.go
+++ b/platformapi/jdapi/sku_test.go
@@ -58,7 +58,11 @@ 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(&QuerySkuParam{
+ IsFilterDel: IsFilterDelTrue,
+ })
+ t.Log(utils.Format4Output(result, false))
+ t.Log(totalCount)
if err != nil {
t.Fatal(err)
}
@@ -73,7 +77,10 @@ func TestQueryListBySkuIds(t *testing.T) {
2018806493,
2018805873,
}
- result, err := api.QueryListBySkuIds(ids, nil)
+ result, err := api.QueryListBySkuIds(&QueryListBySkuIdsParam{
+ SkuIDs: ids,
+ })
+ t.Log(utils.Format4Output(result, false))
if err != nil {
t.Fatal(err)
}
@@ -152,10 +159,10 @@ func TestGetSpuSaleAttr(t *testing.T) {
func TestBatchAddSku(t *testing.T) {
paramList := []*CreateByUpcParam{
&CreateByUpcParam{
- Upc: "6948939649102",
- OutSkuId: "50001",
- Price: 213,
- ShopCategoryId: 4247719,
+ UniqueUpc: "6948939649102",
+ OutSku: "50001",
+ JdPrice: "2.13",
+ ShopCategoryID: 4247719,
IsSale: true,
},
}
diff --git a/platformapi/jdapi/store.go b/platformapi/jdapi/store.go
index 71cb7629..e5811e31 100644
--- a/platformapi/jdapi/store.go
+++ b/platformapi/jdapi/store.go
@@ -29,6 +29,19 @@ const (
KeyStandByPhone = "standByPhone"
)
+const (
+ CarrierNoCrowdSourcing = 9966 // 众包
+ CarrierNoSelfDelivery = 2938 // 自送
+ CarrierNoSelfTake = 9999 // 到店自提
+)
+
+const (
+ CoordinateTypeGoogle = 1 // 谷歌
+ CoordinateTypeBaidu = 2 // 百度
+ CoordinateTypeAutonavi = 3 // 高德
+ CoordinateTypeTencent = 4 // 腾讯
+)
+
type CreateShopResult struct {
DeliveryRangeType int `json:"deliveryRangeType"`
CoordinatePoints string `json:"coordinatePoints"`
@@ -44,6 +57,165 @@ type CityInfo struct {
Yn int `json:"yn"`
}
+type OpStoreParams struct {
+ // 以下可用于修改(及查询)
+ StationNo string `json:"stationNo"` // 强制参数,主键
+ StationName string `json:"stationName,omitempty"`
+ OutSystemID string `json:"outSystemId,omitempty"`
+ Mobile string `json:"mobile,omitempty"`
+ Phone string `json:"phone,omitempty"`
+ Lat float64 `json:"lat,omitempty"`
+ Lng float64 `json:"lng,omitempty"`
+ City int `json:"city,omitempty"`
+ County int `json:"county,omitempty"`
+ StationAddress string `json:"stationAddress,omitempty"`
+ Operator string `json:"operator"` // 返回值无
+ ServiceTimeEnd1 int `json:"serviceTimeEnd1,omitempty"`
+ ServiceTimeStart1 int `json:"serviceTimeStart1,omitempty"`
+ ServiceTimeEnd2 int `json:"serviceTimeEnd2,omitempty"`
+ ServiceTimeStart2 int `json:"serviceTimeStart2,omitempty"`
+ DeliveryRangeType int8 `json:"deliveryRangeType,omitempty"` // 返回值无,划分配送服务范围的类型(3、圆心半径、2、不规则多边形(手动画范围))。若更新此字段需传完整值。
+ CoordinateType int `json:"coordinateType,omitempty"` // 返回值无,使用的地图类型(1,谷歌), (2,百度), (3,高德), (4,腾讯)
+ DeliveryRangeRadius int `json:"deliveryRangeRadius,omitempty"` // 返回值无,时效服务范围半径(单位:米)(如果服务范围为类型3的话,该字段有值)
+ CoordinatePoints string `json:"coordinatePoints,omitempty"` // 返回值无,坐标点集合(如果服务范围为类型2的话,该字段有值),每个点以:经度,纬度 的格式表示,用“;”隔开多个点;对于腾讯地图、谷歌地图和高德地图,整个coordinatePoints的长度必须小于2k;对于百度地图,整个coordinatePoints的长度必须小于1k
+ CloseStatus int `json:"closeStatus"` // 0是有意义的值,所以不能是omitempty
+ StoreNotice string `json:"storeNotice,omitempty"`
+ StandByPhone string `json:"standByPhone,omitempty"`
+}
+
+type StoreDetail struct {
+ // 以下可用于修改(及查询)
+ StationNo string `json:"stationNo"` // 强制参数,主键
+ StationName string `json:"stationName,omitempty"`
+ OutSystemID string `json:"outSystemId,omitempty"`
+ Mobile string `json:"mobile,omitempty"`
+ Phone string `json:"phone,omitempty"`
+ Lat float64 `json:"lat,omitempty"`
+ Lng float64 `json:"lng,omitempty"`
+ City int `json:"city,omitempty"`
+ County int `json:"county,omitempty"`
+ StationAddress string `json:"stationAddress,omitempty"`
+ // Operator string `json:"operator"` // 返回值无
+ ServiceTimeEnd1 int `json:"serviceTimeEnd1,omitempty"`
+ ServiceTimeStart1 int `json:"serviceTimeStart1,omitempty"`
+ ServiceTimeEnd2 int `json:"serviceTimeEnd2,omitempty"`
+ ServiceTimeStart2 int `json:"serviceTimeStart2,omitempty"`
+ // DeliveryRangeType int8 `json:"deliveryRangeType,omitempty"` // 返回值无,划分配送服务范围的类型(3、圆心半径、2、不规则多边形(手动画范围))。若更新此字段需传完整值。
+ // CoordinateType int `json:"coordinateType,omitempty"` // 返回值无,使用的地图类型(1,谷歌), (2,百度), (3,高德), (4,腾讯)
+ // DeliveryRangeRadius int `json:"deliveryRangeRadius,omitempty"` // 返回值无,时效服务范围半径(单位:米)(如果服务范围为类型3的话,该字段有值)
+ // CoordinatePoints string `json:"coordinatePoints,omitempty"` // 返回值无,坐标点集合(如果服务范围为类型2的话,该字段有值),每个点以:经度,纬度 的格式表示,用“;”隔开多个点;对于腾讯地图、谷歌地图和高德地图,整个coordinatePoints的长度必须小于2k;对于百度地图,整个coordinatePoints的长度必须小于1k
+ CloseStatus int `json:"closeStatus"` // 0是有意义的值,所以不能是omitempty
+ StoreNotice string `json:"storeNotice,omitempty"`
+ StandByPhone string `json:"standByPhone,omitempty"`
+
+ // 以下仅用于查询
+ AllowRangeOptimized int `json:"allowRangeOptimized"`
+ CacheKey4StoreList string `json:"cacheKey4StoreList"`
+ CarrierNo int `json:"carrierNo"`
+ CityName string `json:"cityName"`
+ Coordinate string `json:"coordinate"`
+ CoordinateAddress string `json:"coordinateAddress"`
+ CountyName string `json:"countyName"`
+ CreatePin string `json:"createPin"`
+ CreateTime *utils.JavaDate `json:"createTime"`
+ ID int64 `json:"id"`
+ IndustryTag int `json:"industryTag"`
+ InnerNoStatus int `json:"innerNoStatus"`
+ IsAutoOrder int `json:"isAutoOrder"` // 是否自动接单,0:是1:否
+ IsMembership int `json:"isMembership"`
+ IsNoPaper int `json:"isNoPaper"`
+ OnlineTime *utils.JavaDate `json:"onlineTime"`
+ OrderAging int `json:"orderAging"`
+ OrderNoticeType int `json:"orderNoticeType"`
+ PreWarehouse int `json:"preWarehouse"`
+ Province int `json:"province"`
+ ProvinceName string `json:"provinceName"`
+ QualifyStatus int `json:"qualifyStatus"`
+ RegularFlag int `json:"regularFlag"`
+ StationDeliveryStatus int `json:"stationDeliveryStatus"`
+ SupportInvoice int `json:"supportInvoice"`
+ SupportOfflinePurchase int `json:"supportOfflinePurchase"`
+ TestMark int `json:"testMark"`
+ TimeAmType int `json:"timeAmType"`
+ TimePmType int `json:"timePmType"`
+ Ts *utils.JavaDate `json:"ts"`
+ UpdatePin string `json:"updatePin"`
+ UpdateTime *utils.JavaDate `json:"updateTime"`
+ VenderID string `json:"venderId"`
+ VenderName string `json:"venderName"`
+ WareType int `json:"wareType"`
+ WhiteDelivery bool `json:"whiteDelivery"`
+ Yn int8 `json:"yn"` // 门店状态,0启用,1禁用
+}
+
+type OrderProdCommentVo struct {
+ CreatePin string `json:"createPin"`
+ CreateTime *utils.JavaDate `json:"createTime"`
+ ID int64 `json:"id"`
+ IsPraise int `json:"isPraise"`
+ Score int `json:"score"`
+ ScoreLevel int `json:"scoreLevel"`
+ SkuID int64 `json:"skuId"`
+ SkuName string `json:"skuName"`
+ UpdatePin string `json:"updatePin"`
+ UpdateTime *utils.JavaDate `json:"updateTime"`
+ Yn int8 `json:"yn"`
+}
+
+type OrderCommentInfo struct {
+ AppVersion string `json:"appVersion"`
+ CreatePin string `json:"createPin"`
+ CreateTime *utils.JavaDate `json:"createTime"`
+ DeliveryCarrierNo string `json:"deliveryCarrierNo"`
+ DeliveryConfirmTime *utils.JavaDate `json:"deliveryConfirmTime"`
+ DeliveryConfirmTime2 *utils.JavaDate `json:"deliveryConfirmTime2"`
+ DeliveryDifTime int `json:"deliveryDifTime"`
+ DeliveryDifTime2 int `json:"deliveryDifTime2"`
+ DeliveryManNo string `json:"deliveryManNo"`
+ DocID interface{} `json:"docId"`
+ ID int64 `json:"id"`
+ IsUpdate int `json:"isUpdate"`
+ OrderID int64 `json:"orderId"`
+ OrderProdCommentVoList []*OrderProdCommentVo `json:"orderProdCommentVoList"`
+ OrderType int `json:"orderType"`
+ OrgCode int `json:"orgCode"`
+ OrgCommentContent string `json:"orgCommentContent"`
+ OrgCommentStatus int `json:"orgCommentStatus"`
+ OrgCommentTime *utils.JavaDate `json:"orgCommentTime"`
+ OrgName string `json:"orgName"`
+ Score3 int `json:"score3"`
+ Score3Content string `json:"score3Content"`
+ Score4 int `json:"score4"`
+ Score4Content string `json:"score4Content"`
+ ScoreLevel int `json:"scoreLevel"`
+ StoreID int64 `json:"storeId"`
+ StoreName string `json:"storeName"`
+ Tags []string `json:"tags"`
+ TagsKey []string `json:"tagsKey"`
+ UpdatePin string `json:"updatePin"`
+ UpdateTime *utils.JavaDate `json:"updateTime"`
+ VenderTageKey []string `json:"venderTageKey"`
+ VenderTags []string `json:"venderTags"`
+ Yn int8 `json:"yn"`
+}
+
+type StoreDeliveryRange struct {
+ Adresses string `json:"adresses"`
+ CreatePin string `json:"createPin"`
+ CreateTime *utils.JavaDate `json:"createTime"`
+ DeliveryRange string `json:"deliveryRange"`
+ DeliveryRangeRadius int `json:"deliveryRangeRadius"`
+ DeliveryRangeType int `json:"deliveryRangeType"`
+ DeliveryServiceType int `json:"deliveryServiceType"`
+ ID int `json:"id"`
+ StationNo string `json:"stationNo"`
+ Ts *utils.JavaDate `json:"ts"`
+ UpdatePin string `json:"updatePin"`
+ UpdateTime *utils.JavaDate `json:"updateTime"`
+ VenderID string `json:"venderId"`
+ Yn int8 `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 {
@@ -69,23 +241,9 @@ func (a *API) GetStationsByVenderId() ([]string, error) {
// 新增不带资质的门店信息接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=194&apiid=93acef27c3aa4d8286d5c8c26b493629
-func (a *API) CreateStore(stationName, phone string, city, county int, stationAddress, userName string, serviceTimeStart1, serviceTimeEnd1 int, lng, lat float64, deliveryRangeType, coordinateType int, standByPhone string, addParams map[string]interface{}) (*CreateShopResult, error) {
- params := map[string]interface{}{
- KeyStationName: stationName,
- KeyPhone: phone,
- KeyCity: city,
- KeyCounty: county,
- KeyStationAddress: stationAddress,
- KeyOperator: utils.GetAPIOperator(userName),
- KeyServiceTimeStart1: serviceTimeStart1,
- KeyServiceTimeEnd2: serviceTimeEnd1,
- KeyLng: lng,
- KeyLat: lat,
- KeyDeliveryRangeType: deliveryRangeType,
- KeyCoordinateType: coordinateType,
- KeyStandByPhone: standByPhone,
- }
- result, err := a.AccessAPINoPage("store/createStore", utils.MergeMaps(params, addParams), nil, nil, func(data map[string]interface{}) (interface{}, error) {
+func (a *API) CreateStore(createParams *OpStoreParams) (*CreateShopResult, error) {
+ createParams.Operator = utils.GetAPIOperator(createParams.Operator)
+ result, err := a.AccessAPINoPage("store/createStore", utils.Struct2MapByJson(createParams), nil, nil, func(data map[string]interface{}) (interface{}, error) {
innerCode := data["code"].(string)
if data["code"] == "0" {
mapData := data["data"].(map[string]interface{})
@@ -102,37 +260,66 @@ func (a *API) CreateStore(stationName, phone string, city, county int, stationAd
// 根据到家门店编码查询门店基本信息接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=194&apiid=4c48e347027146d5a103e851055cb1a7
-func (a *API) GetStoreInfoByStationNo(storeNo string) (map[string]interface{}, error) {
+// func (a *API) GetStoreInfoByStationNo(storeNo string) (map[string]interface{}, error) {
+// result, err := a.AccessAPINoPage("storeapi/getStoreInfoByStationNo", utils.Params2Map("StoreNo", storeNo), nil, nil, nil)
+// if err == nil {
+// return result.(map[string]interface{}), nil
+// }
+// return nil, err
+// }
+
+func (a *API) GetStoreInfoByStationNo2(storeNo string) (storeDetail *StoreDetail, err error) {
result, err := a.AccessAPINoPage("storeapi/getStoreInfoByStationNo", utils.Params2Map("StoreNo", storeNo), nil, nil, nil)
if err == nil {
- return result.(map[string]interface{}), nil
+ err = utils.Map2StructByJson(result, &storeDetail, false)
}
- return nil, err
+ return storeDetail, err
}
// 修改门店基础信息接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=194&apiid=2600369a456446f0921e918f3d15e96a
-func (a *API) UpdateStoreInfo4Open(storeNo, userName string, addParams map[string]interface{}) error {
- jdParams := map[string]interface{}{
- "stationNo": storeNo,
- "operator": utils.GetAPIOperator(userName),
+// func (a *API) UpdateStoreInfo4Open(storeNo, userName string, addParams map[string]interface{}) error {
+// jdParams := map[string]interface{}{
+// "stationNo": storeNo,
+// "operator": utils.GetAPIOperator(userName),
+// }
+// jdParams = utils.MergeMaps(jdParams, addParams)
+// _, err := a.AccessAPINoPage("store/updateStoreInfo4Open", jdParams, nil, nil, nullResultParser)
+// return err
+// }
+
+func (a *API) UpdateStoreInfo4Open2(updateParams *OpStoreParams, modifyCloseStatus bool) (err error) {
+ updateParams.Operator = utils.GetAPIOperator(updateParams.Operator)
+ mapData := utils.Struct2MapByJson(updateParams)
+ if !modifyCloseStatus {
+ delete(mapData, "closeStatus")
}
- jdParams = utils.MergeMaps(jdParams, addParams)
- _, err := a.AccessAPINoPage("store/updateStoreInfo4Open", jdParams, nil, nil, nullResultParser)
+ _, err = a.AccessAPINoPage("store/updateStoreInfo4Open", mapData, nil, nil, nullResultParser)
return err
}
// 根据订单号查询商家门店评价信息接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=194&apiid=bd23397725bb4e74b69e2f2fa1c88d43
-func (a *API) GetCommentByOrderId(orderId int64) (map[string]interface{}, error) {
+// func (a *API) GetCommentByOrderId(orderId int64) (map[string]interface{}, error) {
+// jdParams := map[string]interface{}{
+// "orderId": orderId,
+// }
+// result, err := a.AccessAPINoPage("commentOutApi/getCommentByOrderId", jdParams, nil, nil, genNoPageResultParser("code", "msg", "result", "200"))
+// if err == nil {
+// return result.(map[string]interface{}), nil
+// }
+// return nil, err
+// }
+
+func (a *API) GetCommentByOrderId2(orderId int64) (orderComment *OrderCommentInfo, err error) {
jdParams := map[string]interface{}{
"orderId": orderId,
}
result, err := a.AccessAPINoPage("commentOutApi/getCommentByOrderId", jdParams, nil, nil, genNoPageResultParser("code", "msg", "result", "200"))
if err == nil {
- return result.(map[string]interface{}), nil
+ err = utils.Map2StructByJson(result, &orderComment, false)
}
- return nil, err
+ return orderComment, err
}
// 商家门店评价信息回复接口
@@ -169,15 +356,26 @@ func (a *API) UpdateStoreConfig4Open(stationNo string, isAutoOrder bool) (bool,
// 获取门店配送范围接口
// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=194&apiid=8f6d0ac75d734c68bf5bd2a09f376a78
-func (a *API) GetDeliveryRangeByStationNo(stationNo string) (map[string]interface{}, error) {
+// func (a *API) GetDeliveryRangeByStationNo(stationNo string) (map[string]interface{}, error) {
+// jdParams := map[string]interface{}{
+// "stationNo": stationNo,
+// }
+// result, err := a.AccessAPINoPage("store/getDeliveryRangeByStationNo", jdParams, nil, nil, nil)
+// if err == nil {
+// return result.(map[string]interface{}), nil
+// }
+// return nil, err
+// }
+
+func (a *API) GetDeliveryRangeByStationNo2(stationNo string) (deliveryRange *StoreDeliveryRange, err error) {
jdParams := map[string]interface{}{
"stationNo": stationNo,
}
result, err := a.AccessAPINoPage("store/getDeliveryRangeByStationNo", jdParams, nil, nil, nil)
if err == nil {
- return result.(map[string]interface{}), nil
+ err = utils.Map2StructByJson(result, &deliveryRange, false)
}
- return nil, err
+ return deliveryRange, err
}
// 私有函数
diff --git a/platformapi/jdapi/store_page.go b/platformapi/jdapi/store_page.go
index c4e56f73..b162c194 100644
--- a/platformapi/jdapi/store_page.go
+++ b/platformapi/jdapi/store_page.go
@@ -1,9 +1,13 @@
package jdapi
import (
+ "bytes"
"crypto/md5"
"fmt"
+ "io/ioutil"
+ "mime/multipart"
"net/http"
+ "net/url"
"regexp"
"strings"
@@ -36,9 +40,9 @@ type CorporationInfo struct {
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 {
+ // 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"`
@@ -136,14 +140,108 @@ type PageShopInfo struct {
StoreShareURL string `json:"storeShareUrl"`
}
-var (
- monthSaleNumReg = regexp.MustCompile(`(\d+)([千|万])`)
+type PageSku struct {
+ ButtonEnable bool `json:"buttonEnable"`
+ CatID string `json:"catId"`
+ FixedStatus bool `json:"fixedStatus"`
+ FuncIndicatins string `json:"funcIndicatins"`
+ H5SwichItem struct {
+ IsLeadApp bool `json:"isLeadApp"`
+ } `json:"h5SwichItem"`
+ HasSaleAttr bool `json:"hasSaleAttr"`
+ IconType int `json:"iconType"`
+ Image []*SkuPageImg `json:"image"`
+ InCartCount int `json:"inCartCount"`
+ IsInScope bool `json:"isInScope"`
+ IsRemind bool `json:"isRemind"`
+ MarkingPrice string `json:"markingPrice"`
+ Name string `json:"name"`
+ OrgCode string `json:"orgCode"`
+ Prescription bool `json:"prescription"`
+ PriceUnit string `json:"priceUnit"`
+ ProductComment struct {
+ CommentNum string `json:"commentNum"`
+ GoodRate float64 `json:"goodRate"`
+ GoodRating string `json:"goodRating"`
+ HasMore bool `json:"hasMore"`
+ TotalScore string `json:"totalScore"`
+ } `json:"productComment"`
+ ProductInfoType int `json:"productInfoType"`
+ ProductType int `json:"productType"`
+ ProductTypeEnum string `json:"productTypeEnum"`
+ ShareProductURL string `json:"shareProductUrl"`
+ ShowState int `json:"showState"`
+ ShowStateEnum string `json:"showStateEnum"`
+ ShowStateName string `json:"showStateName"`
+ ShowTimLine bool `json:"showTimLine"`
+ SkuID int64 `json:"skuId"`
+ SkuPriceVO struct {
+ BasicPrice string `json:"basicPrice"`
+ MkPrice string `json:"mkPrice"`
+ Promotion int `json:"promotion"`
+ RealTimePrice string `json:"realTimePrice"`
+ SkuID string `json:"skuId"`
+ } `json:"skuPriceVO"`
+ Standard string `json:"standard"`
+ StoreInfo struct {
+ Show bool `json:"show"`
+ StoreID string `json:"storeId"`
+ } `json:"storeInfo"`
+ Subtitle string `json:"subtitle"`
+ Tags []string `json:"tags"`
+ UserActionSku string `json:"userActionSku"`
+ VenderID string `json:"venderId"`
+}
+
+const (
+ QualifyTypeCompany = "25" // 营业执照
+ QualifyTypePerson = "22" // 身份证,个体工商户要求填
+ QualifyTypeAddInfo = "31" // 附加信息,如果身份证是长期有效,要求身份证背面信息
+
+ SaveQualifyActionTypeCommit = 0 // 提交
+ SaveQualifyActionTypeSave = 1 // 暂时保存
)
-func (a *API) AccessStorePage(fullURL string, params map[string]interface{}, isPost bool) (retVal map[string]interface{}, err error) {
+type QualifyItem struct {
+ QualifyURL string `json:"qualifyUrl"`
+ QualifyType string `json:"qualifyType"`
+ QualifyExpireForever int `json:"qualifyExpireForever"` // 0:永久有性,1:非永久有效(需要填QualifyExpireEnd)
+ QualifyExpireStart string `json:"qualifyExpireStart"`
+ QualifyExpireEnd string `json:"qualifyExpireEnd,omitempty"`
+ QualifyName string `json:"qualifyName,omitempty"`
+ QualifyOwner string `json:"qualifyOwner,omitempty"`
+ LicenceType string `json:"licenceType,omitempty"` // -1
+ QualifyNumber string `json:"qualifyNumber,omitempty"`
+ QualifyAddress string `json:"qualifyAddress,omitempty"`
+ LicenceName string `json:"licenceName,omitempty"`
+ EconKind string `json:"econKind,omitempty"`
+ Scope string `json:"scope,omitempty"`
+}
+
+var (
+ monthSaleNumReg = regexp.MustCompile(`(\d+)([千|万])`)
+ pageExceedLimitCodes = map[string]int{
+ "403": 1,
+ }
+ pageCanRetryCodes = map[string]int{}
+)
+
+const (
+ KeyImgData = "imgData"
+ KeyImgName = "imgName"
+
+ ResultKeyData = "data"
+ ResultKeyResult = "result"
+)
+
+func (a *API) AccessStorePage2(fullURL string, params map[string]interface{}, isPost bool, resultKey string) (retVal interface{}, err error) {
if a.GetCookieCount() == 0 {
return nil, fmt.Errorf("需要设置Store Cookie才能使用此方法")
}
+ imgData := params[KeyImgData]
+ if imgData != nil {
+ delete(params, KeyImgData)
+ }
err = platformapi.AccessPlatformAPIWithRetry(a.client,
func() *http.Request {
var request *http.Request
@@ -151,8 +249,26 @@ func (a *API) AccessStorePage(fullURL string, params map[string]interface{}, isP
request, _ = http.NewRequest(http.MethodGet, utils.GenerateGetURL(fullURL, "", params), nil)
} else {
request, _ = http.NewRequest(http.MethodPost, fullURL, strings.NewReader(utils.Map2URLValues(params).Encode()))
+ if params[KeyImgName] == nil {
+ request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ } else {
+ var b bytes.Buffer
+ w := multipart.NewWriter(&b)
+ if fw, err := w.CreateFormFile("uploadFile", params[KeyImgName].(string)); err != nil {
+ panic(err.Error())
+ } else {
+ fw.Write(imgData.([]byte))
+ }
+ for k, v := range params {
+ // baseapi.SugarLogger.Debug(k, " ", v)
+ w.WriteField(k, url.QueryEscape(fmt.Sprint(v)))
+ }
+ w.Close()
+ // b.WriteString(utils.Map2URLValues(params).Encode())
+ request, _ = http.NewRequest(http.MethodPost, fullURL, &b)
+ request.Header.Set("Content-Type", w.FormDataContentType())
+ }
request.Header.Set("charset", "UTF-8")
- request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
if err != nil {
return nil
@@ -166,15 +282,20 @@ func (a *API) AccessStorePage(fullURL string, params map[string]interface{}, isP
return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil")
}
retVal = jsonResult1
- code := jsonResult1["code"].(string)
+ code, ok := jsonResult1["code"].(string)
+ if !ok {
+ return platformapi.ErrLevelGeneralFail, utils.NewErrorCode(utils.Format4Output(jsonResult1, true), "999")
+ }
if code == ResponseCodeSuccess {
- retVal, _ = jsonResult1["result"].(map[string]interface{})
+ if resultKey != "" {
+ retVal = jsonResult1[resultKey]
+ }
return platformapi.ErrLevelSuccess, nil
}
newErr := utils.NewErrorCode(jsonResult1["msg"].(string), code)
- if _, ok := exceedLimitCodes[code]; ok {
+ if _, ok := pageExceedLimitCodes[code]; ok {
return platformapi.ErrLevelExceedLimit, newErr
- } else if _, ok := canRetryCodes[code]; ok {
+ } else if _, ok := pageCanRetryCodes[code]; ok {
return platformapi.ErrLevelRecoverableErr, newErr
} else {
baseapi.SugarLogger.Debugf("jd AccessStorePage failed, jsonResult1:%s", utils.Format4Output(jsonResult1, true))
@@ -184,6 +305,14 @@ func (a *API) AccessStorePage(fullURL string, params map[string]interface{}, isP
return retVal, err
}
+func (a *API) AccessStorePage(fullURL string, params map[string]interface{}, isPost bool) (retVal map[string]interface{}, err error) {
+ result, err := a.AccessStorePage2(fullURL, params, isPost, ResultKeyResult)
+ if err == nil {
+ retVal, _ = result.(map[string]interface{})
+ }
+ return retVal, err
+}
+
func (a *API) GetRealMobile4Order(orderId, stationNo string) (mobile string, err error) {
retVal, err := a.GetStoreOrderInfo(orderId, stationNo)
if err == nil {
@@ -202,7 +331,7 @@ func (a *API) GetStoreOrderInfo(orderId, stationNo string) (storeOrderInfo map[s
if stationNo != "" {
params["stationNo"] = stationNo
}
- retVal, err := a.AccessStorePage("http://store.jddj.com/order/newManager/search", params, false)
+ retVal, err := a.AccessStorePage("http://order.jddj.com/order/newManager/search", params, false)
// baseapi.SugarLogger.Debug(utils.Format4Output(retVal, false))
if err == nil {
newOrderinfoMains := retVal["newOrderinfoMains"].(map[string]interface{})
@@ -229,7 +358,7 @@ func (a *API) GetStoreOrderInfoList(fromTime, toTime string) (storeOrderList []m
for {
params["pageNo"] = pageNo
- retVal, err := a.AccessStorePage("http://store.jddj.com/order/newManager/tabQuery/all", params, false)
+ retVal, err := a.AccessStorePage("http://order.jddj.com/order/newManager/tabQuery/all", params, false)
// baseapi.SugarLogger.Debug(utils.Format4Output(retVal, false))
if err == nil {
newOrderinfoMains := retVal["newOrderinfoMains"].(map[string]interface{})
@@ -246,7 +375,7 @@ func (a *API) GetStoreOrderInfoList(fromTime, toTime string) (storeOrderList []m
return nil, err
}
-func (a *API) GetSkuPageInfo(skuId int64) (skuPageInfo map[string]interface{}, err error) {
+func (a *API) GetSkuPageInfo(skuId int64) (skuPageInfo *PageSku, err error) {
skuIDMap := map[string]interface{}{
"skuId": utils.Int64ToStr(skuId),
"storeId": "0",
@@ -258,14 +387,17 @@ func (a *API) GetSkuPageInfo(skuId int64) (skuPageInfo map[string]interface{}, e
"body": utils.Format4Output(skuIDMap, true),
}
- skuPageInfo, err = a.AccessStorePage("https://daojia.jd.com/client", params, false)
+ result, err := a.AccessStorePage("https://daojia.jd.com/client", params, false)
+ if err == nil {
+ err = utils.Map2StructByJson(result, &skuPageInfo, false)
+ }
return skuPageInfo, err
}
func (a *API) GetSkuPageImageInfo(skuId int64) (imgList []*SkuPageImg, err error) {
skuPageInfo, err := a.GetSkuPageInfo(skuId)
if err == nil {
- err = utils.Map2StructByJson(skuPageInfo["image"], &imgList, false)
+ imgList = skuPageInfo.Image
}
return imgList, err
}
@@ -346,3 +478,47 @@ func MonthSaleNum2Int(monthSaleNumStr string) (monthSaleNum int) {
}
return monthSaleNum
}
+
+func (a *API) StoreUploadImg(imgFileName string, imgBin []byte) (imgURL string, err error) {
+ result, err := a.AccessStorePage2("https://sta-store.jddj.com/store/uploadImg.json", map[string]interface{}{
+ KeyImgData: imgBin,
+ KeyImgName: imgFileName,
+ }, true, ResultKeyData)
+ if err == nil {
+ imgURL = result.(string)
+ }
+ return imgURL, err
+}
+
+func (a *API) StoreUploadImgByURL(inImgURL string) (imgURL string, err error) {
+ response, err := http.Get(inImgURL)
+ if err == nil {
+ defer func() {
+ response.Body.Close()
+ }()
+ if response.StatusCode == http.StatusOK {
+ bodyData, err2 := ioutil.ReadAll(response.Body)
+ if err = err2; err == nil {
+ imgName := utils.GetUUID()
+ if lastSlashIndex := strings.LastIndex(inImgURL, "/"); lastSlashIndex >= 0 {
+ imgName = inImgURL[lastSlashIndex+1:]
+ }
+ return a.StoreUploadImg(imgName, bodyData)
+ }
+ } else {
+ err = platformapi.ErrHTTPCodeIsNot200
+ }
+ }
+ return "", err
+}
+
+func (a *API) SaveQualify(stationNo string, actionType int, qualifyList []*QualifyItem) (err error) {
+ _, err = a.AccessStorePage2("https://sta-store.jddj.com/store/saveQualify.o2o", map[string]interface{}{
+ "stationNo": stationNo,
+ "actionType": actionType,
+ "qualifyList": utils.Format4Output(qualifyList, true),
+ "type": 1,
+ "degrade": "no",
+ }, true, "")
+ return err
+}
diff --git a/platformapi/jdapi/store_page_test.go b/platformapi/jdapi/store_page_test.go
index 5e773b19..cc2f4045 100644
--- a/platformapi/jdapi/store_page_test.go
+++ b/platformapi/jdapi/store_page_test.go
@@ -8,9 +8,9 @@ import (
)
func TestGetRealMobileNumber4Order(t *testing.T) {
- orderId := "910170516000941"
- desiredMobile := "13398196274"
- mobile, err := api.GetRealMobile4Order(orderId, "")
+ orderId := "921823424000122"
+ desiredMobile := "13722455105"
+ mobile, err := api.GetRealMobile4Order(orderId, "11893205")
if err != nil {
t.Fatal(err)
}
@@ -21,8 +21,8 @@ func TestGetRealMobileNumber4Order(t *testing.T) {
}
func TestGetStoreOrderInfo(t *testing.T) {
- orderId := "910170516000941"
- orderInfo, err := api.GetStoreOrderInfo(orderId, "")
+ orderId := "921823424000122"
+ orderInfo, err := api.GetStoreOrderInfo(orderId, "11893205")
if err != nil {
t.Fatal(err)
}
@@ -56,7 +56,7 @@ func TestGetSkuPageImageInfo(t *testing.T) {
}
func TestGetCorporationInfo(t *testing.T) {
- imgList, err := api.GetCorporationInfo("", "915101003431062533")
+ imgList, err := api.GetCorporationInfo("", "92330104MA28XPXH5G")
if err != nil {
t.Fatal(err)
}
@@ -101,3 +101,91 @@ func TestMonthSaleNum2Int(t *testing.T) {
t.Fatalf("num3:%d", num3)
}
}
+
+func TestStoreUploadImgByURL(t *testing.T) {
+ outImgURL, err := api.StoreUploadImgByURL("http://image.jxc4.com/940c151db7e396f2ceaec0304597836f.jpg")
+ t.Log(outImgURL)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestSaveQualify(t *testing.T) {
+ jsonStr := `
+ [
+ {
+ "qualifyUrl":"http://img30.360buyimg.com/vendersettle/jfs/t1/69834/24/6602/68812/5d4d35fdEaaf373c6/5c1c50e7bb6330e4.jpg",
+ "qualifyType":"25",
+ "qualifyExpireForever":0,
+ "qualifyExpireStart":"2017-09-07 00:00:00",
+ "qualifyName":"刘男",
+ "licenceType":"-1",
+ "qualifyNumber":"92330104MA28XPXH5G",
+ "qualifyAddress":"浙江省杭州市江干区八堡家园5排10号一楼102",
+ "licenceName":"杭州市江干区刘男便利店",
+ "econKind":"个体工商户",
+ "scope":"食品经营(凭有效许可证经营);零售:卷烟、雪茄烟(凭有效许可证经营);批发、零售:日用百货,五金。(依法须经批准的项目,经相关部门批准后方可开展经营活动)"
+ },
+ {
+ "qualifyUrl":"http://img30.360buyimg.com/vendersettle/jfs/t1/58554/26/7134/19343/5d4d3639E57b14138/bcce25e1eac11be8.jpg",
+ "qualifyType":"22",
+ "qualifyExpireForever":1,
+ "qualifyExpireStart":"2013-07-22 16:59:38",
+ "qualifyExpireEnd":"2033-07-22 16:59:50",
+ "qualifyOwner":"刘男",
+ "qualifyNumber":"420621198110303336"
+ },
+ {
+ "qualifyUrl":"",
+ "qualifyType":"33",
+ "qualifyExpireForever":1,
+ "qualifyExpireStart":"",
+ "qualifyExpireEnd":""
+ },
+ {
+ "qualifyUrl":"",
+ "qualifyType":"8",
+ "qualifyExpireForever":1,
+ "qualifyExpireStart":"",
+ "qualifyExpireEnd":""
+ },
+ {
+ "qualifyUrl":"",
+ "qualifyType":"9",
+ "qualifyExpireForever":1,
+ "qualifyExpireStart":"",
+ "qualifyExpireEnd":""
+ },
+ {
+ "qualifyUrl":"",
+ "qualifyType":"10",
+ "qualifyExpireForever":1,
+ "qualifyExpireStart":"",
+ "qualifyExpireEnd":""
+ },
+ {
+ "qualifyUrl":"",
+ "qualifyType":"29",
+ "qualifyExpireForever":1,
+ "qualifyExpireStart":"",
+ "qualifyExpireEnd":""
+ },
+ {
+ "qualifyUrl":"",
+ "qualifyType":"31",
+ "qualifyExpireForever":1,
+ "qualifyExpireStart":"",
+ "qualifyExpireEnd":""
+ }
+ ]
+ `
+ var qualityList []*QualifyItem
+ err := utils.UnmarshalUseNumber([]byte(jsonStr), &qualityList)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = api.SaveQualify("11902261", SaveQualifyActionTypeSave, qualityList)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/platformapi/jdapi/store_sku.go b/platformapi/jdapi/store_sku.go
index 08ae1886..cee2f4cf 100644
--- a/platformapi/jdapi/store_sku.go
+++ b/platformapi/jdapi/store_sku.go
@@ -9,6 +9,8 @@ import (
const (
MaxStoreSkuBatchSize = 50
MaxStockQty = 100000000
+
+ MaxAddByStoreAndSkusCount = 30 // 批量置顶商品排序接口的最大个数
)
type SkuPriceInfo struct {
@@ -109,7 +111,7 @@ func (a *API) UpdateVendorStationPrice(trackInfo string, outStationNo, stationNo
result, err := a.AccessAPINoPage2("venderprice/updateStationPrice", jdParams, nil, nil, genNoPageResultParser("code", "msg", "result", "0"), trackInfo)
if result != nil {
var err2 error
- if responseList, err2 = a.handleBatchOpResult(len(skuPriceInfoList), result, "json2"); err2 != nil && err == nil {
+ if responseList, err2 = a.handleBatchOpResult(len(skuPriceInfoList), err, result, "json2"); err2 != nil && err == nil {
err = err2
}
}
@@ -146,18 +148,20 @@ func (a *API) GetStationInfoList(stationNo string, skuIds []int64) (priceInfo []
return priceInfo, err
}
-func (a *API) handleBatchOpResult(batchCount int, result interface{}, tagName string) (responseList []*StoreSkuBatchUpdateResponse, err error) {
- if err = utils.Map2Struct(result, &responseList, true, tagName); err == nil {
- var failedList []*StoreSkuBatchUpdateResponse
- for _, v := range responseList {
- if v.Code != 0 {
- failedList = append(failedList, v)
+func (a *API) handleBatchOpResult(batchCount int, inErr error, result interface{}, tagName string) (responseList []*StoreSkuBatchUpdateResponse, err error) {
+ if result != nil {
+ if err = utils.Map2Struct(result, &responseList, true, tagName); err == nil {
+ var failedList []*StoreSkuBatchUpdateResponse
+ for _, v := range responseList {
+ if v.Code != 0 {
+ failedList = append(failedList, v)
+ }
+ }
+ if len(failedList) >= batchCount {
+ err = utils.NewErrorCode(string(utils.MustMarshal(failedList)), ResponseCodeAccessFailed, 1) // 此错误基本用不到
+ } else if len(failedList) > 0 { // 部分失败
+ err = utils.NewErrorCode(string(utils.MustMarshal(failedList)), ResponseInnerCodePartialFailed, 1)
}
- }
- if len(failedList) >= batchCount {
- err = utils.NewErrorCode(string(utils.MustMarshal(failedList)), ResponseCodeAccessFailed, 1) // 此错误基本用不到
- } else if len(failedList) > 0 { // 部分失败
- err = utils.NewErrorCode(string(utils.MustMarshal(failedList)), ResponseInnerCodePartialFailed, 1)
}
}
return responseList, err
@@ -181,7 +185,7 @@ func (a *API) BatchUpdateCurrentQtys(trackInfo, outStationNo, stationNo string,
result, err := a.AccessAPINoPage2("stock/batchUpdateCurrentQtys", jdParams, nil, nil, genNoPageResultParser("retCode", "retMsg", "data", "0"), trackInfo)
if result != nil {
var err2 error
- if responseList, err2 = a.handleBatchOpResult(len(skuStockList), result, ""); err2 != nil && err == nil {
+ if responseList, err2 = a.handleBatchOpResult(len(skuStockList), err, result, ""); err2 != nil && err == nil {
err = err2
}
}
@@ -231,7 +235,7 @@ func (a *API) UpdateVendibility(trackInfo string, listBaseStockCenterRequest []*
result, err := a.AccessAPINoPage2("stock/updateVendibility", jdParams, nil, nil, genNoPageResultParser("retCode", "retMsg", "data", "0"), trackInfo)
if result != nil {
var err2 error
- if responseList, err2 = a.handleBatchOpResult(len(listBaseStockCenterRequest), result, ""); err2 != nil && err == nil {
+ if responseList, err2 = a.handleBatchOpResult(len(listBaseStockCenterRequest), err, result, ""); err2 != nil && err == nil {
err = err2
}
}
@@ -253,10 +257,11 @@ func (a *API) BatchUpdateVendibility(trackInfo, outStationNo, stationNo string,
} else {
jdParams["stationNo"] = stationNo
}
+ // 此函数在全部失败时,err仍然返回成功
result, err := a.AccessAPINoPage2("stock/batchUpdateVendibility", jdParams, nil, nil, genNoPageResultParser("retCode", "retMsg", "data", "0"), trackInfo)
if result != nil {
var err2 error
- if responseList, err2 = a.handleBatchOpResult(len(stockVendibilityList), result, ""); err2 != nil && err == nil {
+ if responseList, err2 = a.handleBatchOpResult(len(stockVendibilityList), err, result, ""); err2 != nil && err == nil {
err = err2
}
}
@@ -291,3 +296,14 @@ func (a *API) QueryStockCenter(outStationNo string, skuIds []*SkuIdEntity, userP
}
return vendibilityResponse, err
}
+
+// 批量置顶商品排序接口
+// https://opendj.jd.com/staticnew/widgets/resources.html?groupid=180&apiid=a7677f1a75984ed3a6ea3d4827f5a6b4
+func (a *API) AddByStoreAndSkus(stationNo int64, skuIDs []int64) (err error) {
+ jdParams := map[string]interface{}{
+ "storeId": stationNo,
+ "skuIds": skuIDs,
+ }
+ _, err = a.AccessAPINoPage("OrgSortService/addByStoreAndSkus", jdParams, nil, nil, genNoPageResultParser("status", "message", "", "200"))
+ return err
+}
diff --git a/platformapi/jdapi/store_test.go b/platformapi/jdapi/store_test.go
index 2b62eeab..47f5d5f6 100644
--- a/platformapi/jdapi/store_test.go
+++ b/platformapi/jdapi/store_test.go
@@ -11,6 +11,9 @@ import (
const (
mustExistStoreID = "11053496"
mustExistStoreJXID = "2"
+
+ // mustExistStoreID = "11734851"
+ // mustExistStoreJXID = "100118"
)
func TestGetAllCities(t *testing.T) {
@@ -39,56 +42,51 @@ func TestGetStationsByVenderId(t *testing.T) {
}
func TestGetStoreInfoByStationNo(t *testing.T) {
- result, err := api.GetStoreInfoByStationNo(mustExistStoreID)
+ result, err := api.GetStoreInfoByStationNo2(mustExistStoreID)
if err != nil {
t.Fatal(err)
}
- outSystemId := result["outSystemId"].(string)
- if outSystemId != "100285" {
- baseapi.SugarLogger.Fatalf("outSystemId is not correct, its:%s", outSystemId)
+ t.Log(utils.Format4Output(result, false))
+ if result.OutSystemID != mustExistStoreJXID {
+ baseapi.SugarLogger.Fatalf("outSystemId is not correct, its:%s", result.OutSystemID)
}
}
func TestUpdateStoreInfo4Open(t *testing.T) {
- result, err := api.GetStoreInfoByStationNo(mustExistStoreID)
+ result, err := api.GetStoreInfoByStationNo2(mustExistStoreID)
if err != nil {
t.Fatal(err)
}
- oldAddress := result["stationAddress"].(string)
- testAddress := oldAddress + "T"
- addParams := map[string]interface{}{
- "stationAddress": testAddress,
+ oldAddress := result.StationAddress
+ params := &OpStoreParams{
+ StationNo: mustExistStoreID,
+ Operator: "test",
+ StationAddress: oldAddress + "T",
}
- err = api.UpdateStoreInfo4Open(mustExistStoreID, "test", addParams)
+ err = api.UpdateStoreInfo4Open2(params, false)
if err != nil {
t.Fatal(err)
}
- result, err = api.GetStoreInfoByStationNo(mustExistStoreID)
- newAddress := result["stationAddress"].(string)
- if newAddress != testAddress {
+ result, err = api.GetStoreInfoByStationNo2(mustExistStoreID)
+ newAddress := result.StationAddress
+ if newAddress != params.StationAddress {
t.Fatalf("address not match, newAddress:%s, oldAddress:%s", newAddress, oldAddress)
}
- addParams = map[string]interface{}{
- "stationAddress": oldAddress,
- }
- api.UpdateStoreInfo4Open(mustExistStoreID, "test", addParams)
+ params.StationAddress = oldAddress
+ api.UpdateStoreInfo4Open2(params, false)
if err != nil {
t.Fatal(err)
}
-
}
+
func TestGetCommentByOrderId(t *testing.T) {
- testOrderID := int64(822347450000922)
- result, err := api.GetCommentByOrderId(testOrderID)
+ testOrderID := int64(922520919000622)
+ result, err := api.GetCommentByOrderId2(testOrderID)
if err != nil {
t.Fatal(err.Error())
}
- gotOrderID := utils.MustInterface2Int64(result["orderId"])
- if gotOrderID != testOrderID {
- t.Fatalf("GetCommentByOrderId wrong, gotOrderID:%d", gotOrderID)
- }
t.Log(utils.Format4Output(result, false))
}
@@ -104,38 +102,38 @@ func TestUpdateStoreConfig4Open(t *testing.T) {
t.Fatal(result)
}
time.Sleep(2 * time.Second)
- result2, err := api.GetStoreInfoByStationNo(testStationNo)
+ result2, err := api.GetStoreInfoByStationNo2(testStationNo)
if err != nil {
t.Fatal(err.Error())
}
- isAutoOrder := int(utils.MustInterface2Int64(result2["isAutoOrder"]))
+ isAutoOrder := result2.IsAutoOrder
if isAutoOrder != 0 && desiredValue || isAutoOrder == 0 && !desiredValue {
t.Fatalf("UpdateStoreConfig4Open failed, isAutoOrder:%d", isAutoOrder)
}
}
func TestGetDeliveryRangeByStationNo(t *testing.T) {
- const testStoreID = "11738152"
- result, err := api.GetDeliveryRangeByStationNo(testStoreID)
- if err != nil {
- t.Fatal(err)
- }
- result2, err := api.GetStoreInfoByStationNo(testStoreID)
+ const testStoreID = "11734851"
+ result, err := api.GetDeliveryRangeByStationNo2(testStoreID)
if err != nil {
t.Fatal(err)
}
baseapi.SugarLogger.Debug(utils.Format4Output(result, false))
- baseapi.SugarLogger.Debug(utils.Format4Output(result2, false))
- deliveryRange := result["deliveryRange"].(string)
- params := map[string]interface{}{
- "lng": result2["lng"],
- "lat": result2["lat"],
- "coordinateType": 3,
- "deliveryRangeType": 2,
- "coordinatePoints": deliveryRange,
- }
- err = api.UpdateStoreInfo4Open(testStoreID, "test", params)
+}
+
+func TestDisableAutoOrder4AllStores(t *testing.T) {
+ storeIDs, err := api.GetStationsByVenderId()
if err != nil {
t.Fatal(err)
}
+ for _, storeID := range storeIDs {
+ if storeInfo, err := api.GetStoreInfoByStationNo2(storeID); err == nil {
+ if storeInfo.Yn == 0 && storeInfo.IsAutoOrder == 0 {
+ t.Log(storeID)
+ api.UpdateStoreConfig4Open(storeID, false)
+ }
+ t.Log(utils.Format4Output(storeInfo, false))
+ }
+ break
+ }
}
diff --git a/platformapi/mtpsapi/mtpsapi_test.go b/platformapi/mtpsapi/mtpsapi_test.go
index d053345c..c8731a7f 100644
--- a/platformapi/mtpsapi/mtpsapi_test.go
+++ b/platformapi/mtpsapi/mtpsapi_test.go
@@ -25,7 +25,7 @@ func init() {
// prod
// api = New("3c0a05d464c247c19d7ec13accc78605", "b1M}9?:sTbsB[OF2gNORnN(|(iy9rB8(`7]|[wGLnbmt`evfM>E:A90DjHAW:UPE")
- api.SetCookie("token", "0seqGSJnhbr4XJ0EaIQL6CoOpnaV1ErgS42uOlzNXYIX7PeuLuyCFQQZKKWGExJ7IMTQQQDe5H6YMmVFnxjCkw")
+ api.SetCookie("token", "M0p9VatZSeSHfrosD5IViAVl73IcA8mlcuHIV5sG6Zpv83a7JE0wY3t26aEhrrs_MR5gtLSFF1UIkt8HAjaXow")
}
func handleError(t *testing.T, err error) {
diff --git a/platformapi/mtwmapi/mtwmapi.go b/platformapi/mtwmapi/mtwmapi.go
index 3aa53dff..e6aa16be 100644
--- a/platformapi/mtwmapi/mtwmapi.go
+++ b/platformapi/mtwmapi/mtwmapi.go
@@ -9,7 +9,6 @@ import (
"net/url"
"sort"
"strings"
- "sync"
"time"
"git.rosy.net.cn/baseapi"
@@ -45,10 +44,12 @@ const (
ErrCodeSysErr = 700 // 系统错误,按美团外卖技术支持的说法,可当成需重试的错误
ErrCodeAccessLimited = 711 // 接口调用过于频繁,触发流控,请降低调用频率
- ErrCodeNoAppFood = 805 // 不存在此菜品
- ErrCodeNoSuchOrder = 808 // 不存在此订单
- ErrCodeSkuCategoryNotExist = 1021 // 菜品分类不存在
- ErrCodeSkuCategoryExist = 1037 // 菜品分类已存在
+ ErrCodeNoAppFood = 805 // 不存在此菜品
+ ErrCodeNoSuchOrder = 806 // 不存在此订单
+ ErrCodeOpFailed = 808 // 操作失败(如订单在操作时,状态已变更等情况)
+ ErrCodeSkuCategoryNotExist = 1021 // 菜品分类不存在
+ ErrCodeSkuCategoryExist = 1037 // 菜品分类已存在
+ ErrCodeCanNotModifyStoreDeliveryInfo = 3018 // 商家已接入美团配送,不可修改门店配送相关信息
)
type API struct {
@@ -59,16 +60,15 @@ type API struct {
callbackURL string
client *http.Client
config *platformapi.APIConfig
-
- locker sync.RWMutex
- userCookies map[string]string
}
var (
canRetryCodes = map[int]int{
- ErrCodeSysErr: 1,
ErrCodeAccessLimited: 1,
}
+ canRecoverCodes = map[int]int{
+ ErrCodeSysErr: 1,
+ }
)
func New(appID, secret, callbackURL string, config ...*platformapi.APIConfig) *API {
@@ -82,8 +82,6 @@ func New(appID, secret, callbackURL string, config ...*platformapi.APIConfig) *A
callbackURL: callbackURL,
client: &http.Client{Timeout: curConfig.ClientTimeout},
config: &curConfig,
-
- userCookies: make(map[string]string),
}
}
@@ -145,7 +143,7 @@ func (a *API) AccessAPI2(cmd string, isGet bool, bizParams map[string]interface{
fw.Write(imgData.([]byte))
}
for k, v := range params {
- fmt.Println(k, " ", v)
+ // baseapi.SugarLogger.Debug(k, " ", v)
w.WriteField(k, url.QueryEscape(fmt.Sprint(v)))
}
w.Close()
@@ -173,6 +171,8 @@ func (a *API) AccessAPI2(cmd string, isGet bool, bizParams map[string]interface{
newErr := utils.NewErrorIntCode(errorInfo["msg"].(string), int(utils.MustInterface2Int64(errorInfo["code"])))
if canRetryCodes[newErr.IntCode()] == 1 {
return platformapi.ErrLevelExceedLimit, newErr
+ } else if canRecoverCodes[newErr.IntCode()] == 1 {
+ return platformapi.ErrLevelRecoverableErr, newErr
}
return platformapi.ErrLevelCodeIsNotOK, newErr
}
diff --git a/platformapi/mtwmapi/order.go b/platformapi/mtwmapi/order.go
index c3498b0e..803ee838 100644
--- a/platformapi/mtwmapi/order.go
+++ b/platformapi/mtwmapi/order.go
@@ -67,6 +67,11 @@ const (
NotifyTypeCancelRefundComplaint = "cancelRefundComplaint" // 用户取消退款申诉
)
+const (
+ OrderPickTypeNormal = 0 // 普通(配送)
+ OrderPickTypeSelf = 1 // 用户到店自取
+)
+
const (
UserApplyCancelWaitMinute = 30 // 用户申请退款后,商家未在30分钟内(大连锁商家为3小时内)处理退款请求,系统将自动同意退款
)
@@ -308,6 +313,23 @@ type GetOrderIdByDaySeqResult struct {
OrderIDs []int64 `json:"order_ids"`
}
+type UserRealPhoneNumberInfo struct {
+ OrderID int64 `json:"order_id"`
+ AppPoiCode string `json:"app_poi_code"`
+ WmOrderIDView string `json:"wm_order_id_view"`
+ DaySeq int `json:"day_seq"`
+ RealPhoneNumber string `json:"real_phone_number"` // 订单收货人的真实手机号码
+ RealOrderPhoneNumber string `json:"real_order_phone_number"` // 鲜花绿植类订单预订人的真实手机号码,如无则返回空。
+}
+
+type RiderRealPhoneNumberInfo struct {
+ OrderID int64 `json:"order_id"`
+ AppPoiCode string `json:"app_poi_code"`
+ WmOrderIDView string `json:"wm_order_id_view"`
+ RiderName string `json:"rider_name"`
+ RiderRealPhoneNumber string `json:"rider_real_phone_number"` // 骑手真实手机号
+}
+
func (a *API) OrderReceived(orderID int64) (err error) {
_, err = a.AccessAPI("order/poi_received", true, map[string]interface{}{
KeyOrderID: orderID,
@@ -462,8 +484,10 @@ func (a *API) OrderLogisticsStatus(orderID int64) (status map[string]interface{}
return nil, err
}
+// 拉取用户真实手机号(必接)
+// https://developer.waimai.meituan.com/home/docDetail/222
// limit最大为MaxBatchPullPhoneNumberLimit = 1000
-func (a *API) OrderBatchPullPhoneNumber(poiCode string, offset, limit int) (realNumberList []map[string]interface{}, err error) {
+func (a *API) OrderBatchPullPhoneNumber(poiCode string, offset, limit int) (realNumberList []*UserRealPhoneNumberInfo, err error) {
params := map[string]interface{}{
"offset": offset,
"limit": limit,
@@ -473,9 +497,27 @@ func (a *API) OrderBatchPullPhoneNumber(poiCode string, offset, limit int) (real
}
result, err := a.AccessAPI("order/batchPullPhoneNumber", false, params)
if err == nil {
- return utils.Slice2MapSlice(result.([]interface{})), nil
+ err = utils.Map2StructByJson(result, &realNumberList, false)
}
- return nil, err
+ return realNumberList, err
+}
+
+// 拉取骑手真实手机号(必接),美团2019-09-17才开始灰度上线
+// https://developer.waimai.meituan.com/home/docDetail/388
+// limit最大为MaxBatchPullPhoneNumberLimit = 1000
+func (a *API) OrderGetRiderInfoPhoneNumber(poiCode string, offset, limit int) (realNumberList []*RiderRealPhoneNumberInfo, err error) {
+ params := map[string]interface{}{
+ "offset": offset,
+ "limit": limit,
+ }
+ if poiCode != "" {
+ params[KeyAppPoiCode] = poiCode
+ }
+ result, err := a.AccessAPI("order/getRiderInfoPhoneNumber", false, params)
+ if err == nil {
+ err = utils.Map2StructByJson(result, &realNumberList, false)
+ }
+ return realNumberList, err
}
// 专快混配送转为商家自配送
diff --git a/platformapi/mtwmapi/order_test.go b/platformapi/mtwmapi/order_test.go
index 838ac1d5..444d3130 100644
--- a/platformapi/mtwmapi/order_test.go
+++ b/platformapi/mtwmapi/order_test.go
@@ -80,14 +80,19 @@ func TestOrderLogisticsStatus(t *testing.T) {
}
func TestOrderBatchPullPhoneNumber(t *testing.T) {
- result, err := api.OrderBatchPullPhoneNumber(testPoiCode, 0, 10)
+ result, err := api.OrderBatchPullPhoneNumber(testPoiCode, 0, MaxBatchPullPhoneNumberLimit)
+ t.Log(utils.Format4Output(result, false))
if err != nil {
t.Fatal(err)
}
- if len(result) == 0 {
- t.Fatal("result should have value")
+}
+
+func TestOrderGetRiderInfoPhoneNumber(t *testing.T) {
+ result, err := api.OrderGetRiderInfoPhoneNumber(testPoiCode, 0, MaxBatchPullPhoneNumberLimit)
+ t.Log(utils.Format4Output(result, false))
+ if err != nil {
+ t.Fatal(err)
}
- // t.Log(utils.Format4Output(result, false))
}
func TestGetOrderRefundDetail(t *testing.T) {
diff --git a/platformapi/mtwmapi/poi.go b/platformapi/mtwmapi/poi.go
index bc407d74..a2263a19 100644
--- a/platformapi/mtwmapi/poi.go
+++ b/platformapi/mtwmapi/poi.go
@@ -47,6 +47,9 @@ type PoiInfo struct {
Utime int64 `json:"utime,omitempt"`
}
+// todo 此函数在open_level与is_online开店,由于一些原因并没有成功时,好像并不会报错
+// 经常会奇怪的报错:商家已接入美团配送,不可修改门店配送相关信息,但实际并没有修改任何与配送相关的东西
+// 参见:https://developer.waimai.meituan.com/home/myquestionDetail/6194
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
@@ -150,6 +153,8 @@ func (a *API) PoiShipTimeUpdate(poiCode, shippingTime string) (err error) {
}
// 美团要求必须是jpg|jpeg图片格式,且imgName必须以jpg或jpeg结尾
+// 此接口虽然要求poiCode参数,但经过测试,发现上传的图片可以跨门店使用
+// imgName好像不支持中文,否则会报签名错
func (a *API) ImageUpload(poiCode, imgName string, imgData []byte) (imgID string, err error) {
result, err := a.AccessAPI("image/upload", false, map[string]interface{}{
KeyAppPoiCode: poiCode,
diff --git a/platformapi/mtwmapi/poi_test.go b/platformapi/mtwmapi/poi_test.go
index e8874199..a089a5d9 100644
--- a/platformapi/mtwmapi/poi_test.go
+++ b/platformapi/mtwmapi/poi_test.go
@@ -22,7 +22,8 @@ func TestPoiGetIDs(t *testing.T) {
}
func TestPoiMGet(t *testing.T) {
- result, err := api.PoiMGet([]string{testPoiCode})
+ result, err := api.PoiMGet([]string{"2461723"})
+ t.Log(utils.Format4Output(result, false))
if err != nil {
t.Fatal(err)
}
@@ -32,7 +33,6 @@ func TestPoiMGet(t *testing.T) {
if result[0].AppPoiCode != testPoiCode {
t.Fatal("test_poi_01 is not equal")
}
- t.Log(utils.Format4Output(result, false))
}
func TestPoiSave(t *testing.T) {
@@ -80,3 +80,21 @@ func TestPoiStatus(t *testing.T) {
t.Fatal(err)
}
}
+
+func TestPoiShipTimeUpdate(t *testing.T) {
+ err := api.PoiShipTimeUpdate("7174130", "00:00-23:00")
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = api.PoiOpen("6741258")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestPoiOpen(t *testing.T) {
+ err := api.PoiOpen("6735933")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/platformapi/mtwmapi/retail.go b/platformapi/mtwmapi/retail.go
index 856c2cef..fe3249ff 100644
--- a/platformapi/mtwmapi/retail.go
+++ b/platformapi/mtwmapi/retail.go
@@ -113,6 +113,7 @@ type AppFoodResult struct {
// 创建二级分类,secondaryName为二级分类名,
// (如果originName为空,同时创建一级分类,所以如果只是创建二级分类,originName与name要填一样的,此时sequence指的二级分类的sequence,一级分类的sequence为缺省值)
// 修改二级分类,originName为二级分类名,name为二级分类新名,secondaryName为空
+// https://developer.waimai.meituan.com/home/questionDetail/4669
func (a *API) RetailCatUpdate(poiCode, originName, name, secondaryName string, sequence int) (err error) {
params := map[string]interface{}{
KeyAppPoiCode: poiCode,
diff --git a/platformapi/platformapi.go b/platformapi/platformapi.go
index 63e04287..7b313a73 100644
--- a/platformapi/platformapi.go
+++ b/platformapi/platformapi.go
@@ -6,6 +6,7 @@ import (
"fmt"
"io"
"io/ioutil"
+ "math"
"net"
"net/http"
"net/url"
@@ -17,18 +18,18 @@ import (
)
const (
- DefClientTimeout = 15 * time.Second
- DefSleepSecondWhenExceedLimit = 3 * time.Second
- DefMaxRecoverableRetryCount = 1
- DefMaxExceedLimitRetryCount = 25
+ DefClientTimeout = 30 * time.Second
+ DefMaxSleepSecondWhenExceedLimit = 62 // 超频类错误最大重试间隙(秒)
+ DefMaxExceedLimitRetryCount = 25 // 超频类错误最大重试次数
+ DefMaxRecoverableRetryCount = 1 // 可恢复类错误(一般指网络错),最大重试次数
KeyTrackInfo = "TrackInfo"
)
type APIRetryConfig struct {
- MaxExceedLimitRetryCount int
- MaxRecoverableRetryCount int
- SleepSecondWhenExceedLimit time.Duration
+ MaxExceedLimitRetryCount int
+ MaxRecoverableRetryCount int
+ MaxSleepSecondWhenExceedLimit int
}
type APIConfig struct {
@@ -45,9 +46,9 @@ type AccessPlatformAPIWithRetryParam struct {
var (
DefAPIConfig = APIConfig{
APIRetryConfig: APIRetryConfig{
- MaxExceedLimitRetryCount: DefMaxExceedLimitRetryCount,
- MaxRecoverableRetryCount: DefMaxRecoverableRetryCount,
- SleepSecondWhenExceedLimit: DefSleepSecondWhenExceedLimit,
+ MaxExceedLimitRetryCount: DefMaxExceedLimitRetryCount,
+ MaxRecoverableRetryCount: DefMaxRecoverableRetryCount,
+ MaxSleepSecondWhenExceedLimit: DefMaxSleepSecondWhenExceedLimit,
},
ClientTimeout: DefClientTimeout,
}
@@ -90,6 +91,11 @@ func getClonedData(requestURL *url.URL, r *bytes.Buffer) string {
return retVal
}
+func NewDefAPIConfig() (conf *APIConfig) {
+ obj := DefAPIConfig
+ return &obj
+}
+
func AccessPlatformAPIWithRetry(client *http.Client, handleRequest func() *http.Request, config *APIConfig, handleResponse func(response *http.Response, bodyStr string, bodyMap map[string]interface{}) (string, error)) error {
exceedLimitRetryCount := 0
recoverableErrorRetryCount := 0
@@ -101,12 +107,10 @@ func AccessPlatformAPIWithRetry(client *http.Client, handleRequest func() *http.
}
beginTime := time.Now()
trackInfo := request.Header.Get(KeyTrackInfo)
- if trackInfo == "" {
- trackInfo = utils.GetUUID()
- // request.Header.Set(KeyTrackInfo, trackID)
- } else {
+ if trackInfo != "" {
request.Header.Del(KeyTrackInfo)
}
+ trackInfo += ", " + utils.GetUUID()
baseapi.SugarLogger.Debugf("begin AccessPlatformAPIWithRetry:%s do:%s url:%v", trackInfo, request.Method, request.URL)
response, err := client.Do(request)
baseapi.SugarLogger.Debugf("end AccessPlatformAPIWithRetry:%s do url:%v, request:%s", trackInfo, request.URL, getClonedData(request.URL, savedBuf))
@@ -174,7 +178,11 @@ func AccessPlatformAPIWithRetry(client *http.Client, handleRequest func() *http.
} else if errLevel == ErrLevelExceedLimit {
exceedLimitRetryCount++
if exceedLimitRetryCount <= config.MaxExceedLimitRetryCount {
- time.Sleep(config.SleepSecondWhenExceedLimit)
+ sleepSeconds := int(math.Exp2(float64(exceedLimitRetryCount)))
+ if sleepSeconds > config.MaxSleepSecondWhenExceedLimit {
+ sleepSeconds = config.MaxSleepSecondWhenExceedLimit
+ }
+ time.Sleep(time.Duration(sleepSeconds) * time.Second)
continue
}
} else if errLevel == ErrLevelRecoverableErr {
diff --git a/platformapi/platformapi_cookie.go b/platformapi/platformapi_cookie.go
index 90a7bd9b..e6dd8998 100644
--- a/platformapi/platformapi_cookie.go
+++ b/platformapi/platformapi_cookie.go
@@ -11,7 +11,7 @@ import (
)
type APICookie struct {
- locker sync.RWMutex
+ sync.RWMutex
storeCookies map[string]string
}
@@ -44,28 +44,28 @@ func (a *APICookie) SetCookieWithStr(cookieStr string) {
}
func (a *APICookie) SetCookie(key, value string) {
- a.locker.Lock()
- defer a.locker.Unlock()
+ a.Lock()
+ defer a.Unlock()
a.createMapIfNeeded()
a.storeCookies[key] = value
}
func (a *APICookie) GetCookie(key string) string {
- a.locker.RLock()
- defer a.locker.RUnlock()
+ a.RLock()
+ defer a.RUnlock()
a.createMapIfNeeded()
return a.storeCookies[key]
}
func (a *APICookie) GetCookieCount() int {
- a.locker.RLock()
- defer a.locker.RUnlock()
+ a.RLock()
+ defer a.RUnlock()
return len(a.storeCookies)
}
func (a *APICookie) FillRequestCookies(r *http.Request) *http.Request {
- a.locker.RLock()
- defer a.locker.RUnlock()
+ a.RLock()
+ defer a.RUnlock()
for k, v := range a.storeCookies {
r.AddCookie(&http.Cookie{
Name: k,
diff --git a/platformapi/yilianyunapi/yilianyunapi.go b/platformapi/yilianyunapi/yilianyunapi.go
index 5019fd25..ef08c085 100644
--- a/platformapi/yilianyunapi/yilianyunapi.go
+++ b/platformapi/yilianyunapi/yilianyunapi.go
@@ -56,6 +56,15 @@ type TokenInfo struct {
MachineCode string `json:"machine_code"`
}
+type PrinterOrderResult struct {
+ Content string `json:"content"`
+ ID string `json:"id"`
+ OriginID string `json:"origin_id"`
+ PrintTime string `json:"print_time"`
+ ReceivingTime string `json:"receiving_time"`
+ Status int `json:"status"`
+}
+
func New(clientID, clientSecret string, config ...*platformapi.APIConfig) *API {
curConfig := platformapi.DefAPIConfig
if len(config) > 0 {
@@ -238,7 +247,6 @@ func (a *API) GetPrinterToken(machineCode, qrKey string) (tokenInfo *TokenInfo,
}, "")
if err == nil {
err = utils.Map2StructByJson(result, &tokenInfo, false)
- a.SetToken(tokenInfo.AccessToken)
}
return tokenInfo, err
}
@@ -257,3 +265,16 @@ func (a *API) CancelAll(machineCode, token string) (err error) {
func (a *API) PlayText(machineCode, orderID, text, token string) (err error) {
return a.printMsg(machineCode, orderID, fmt.Sprintf("", strings.Replace(text, ",", ".", -1)), token)
}
+
+// pageIndex从1开始,pageSize最大为100
+func (a *API) GetOrderPagingList(machineCode, token string, pageIndex, pageSize int) (orderResultList []*PrinterOrderResult, err error) {
+ result, err := a.AccessAPI("printer/getorderpaginglist", map[string]interface{}{
+ "machine_code": machineCode,
+ "page_index": pageIndex,
+ "page_size": pageSize,
+ }, token)
+ if err != nil {
+ err = utils.Map2StructByJson(result, &orderResultList, false)
+ }
+ return orderResultList, err
+}
diff --git a/platformapi/yilianyunapi/yilianyunapi_test.go b/platformapi/yilianyunapi/yilianyunapi_test.go
index 3e3f79f1..f39b0601 100644
--- a/platformapi/yilianyunapi/yilianyunapi_test.go
+++ b/platformapi/yilianyunapi/yilianyunapi_test.go
@@ -21,7 +21,7 @@ func init() {
// 自有应用
api = New("1039586024", "4885d07c2997b661102e4b6099c0bf3b")
- api.SetToken("7884617f9eeb4c28933569f94a95b5c3")
+ api.SetToken("3a38e3cec7974b459a6f0a381c9b0312")
// 开放应用
// api = New("1098307169", "d5eedb40c99e6691b1ca2ba82a363d6a")
@@ -42,19 +42,19 @@ func TestRetrieveToken(t *testing.T) {
func TestAddPrinter(t *testing.T) {
// err := api.AddPrinter("4004600675", "fem2ukwvduik", "公司测试打印机1")
- // 4004617180 公司测试打印机2
- err := api.AddPrinter("4004600675", "fem2ukwvduik", "测试打印机1")
+ // 4004617180, 381870509796 公司测试打印机2
+ err := api.AddPrinter("4004615546", "7nxsw668yqtk", "测试打印机1")
handleError(t, err)
}
func TestDeletePrinter(t *testing.T) {
- err := api.DeletePrinter("4004611945")
+ err := api.DeletePrinter("4004615546")
handleError(t, err)
}
func TestPrintMsg(t *testing.T) {
// 4004606481
- err := api.PrintMsg("4004600675", utils.GetUUID(), "饿百取货码")
+ err := api.PrintMsg("4004615546", utils.GetUUID(), "饿百取货码")
handleError(t, err)
}
@@ -65,13 +65,13 @@ func TestPrintMsgWithToken(t *testing.T) {
}
func TestGetPrintStatus(t *testing.T) {
- state, err := api.GetPrintStatus("4004617180")
+ state, err := api.GetPrintStatus("4004615546")
handleError(t, err)
baseapi.SugarLogger.Debug(state)
}
func TestGetPrintStatusWithToken(t *testing.T) {
- state, err := api.GetPrintStatusWithToken("4004600675", "b1a36aa8de5647d2b08af49db11a9c9d")
+ state, err := api.GetPrintStatusWithToken("4004617242", "88de89a384714436a5d1ca23e8991e15")
handleError(t, err)
baseapi.SugarLogger.Debug(state)
}
@@ -91,3 +91,15 @@ func TestPlayText(t *testing.T) {
err := api.PlayText("4004617180", utils.GetUUID(), "我们已安排%s配送员%s负责配送。^_^", "")
handleError(t, err)
}
+
+func TestRefreshToken(t *testing.T) {
+ tokenInfo, err := api.RefreshToken("c565a8983887488eb2a708ca118ce43c")
+ handleError(t, err)
+ t.Log(utils.Format4Output(tokenInfo, true))
+}
+
+func TestGetOrderPagingList(t *testing.T) {
+ result, err := api.GetOrderPagingList("4004613792", "", 1, 50)
+ handleError(t, err)
+ t.Log(utils.Format4Output(result, true))
+}
diff --git a/utils/typeconv.go b/utils/typeconv.go
index 9e75380a..e81d978d 100644
--- a/utils/typeconv.go
+++ b/utils/typeconv.go
@@ -7,12 +7,13 @@ import (
"fmt"
"math"
"net/url"
+ "reflect"
"strconv"
"strings"
"time"
"git.rosy.net.cn/baseapi"
- "github.com/fatih/structs"
+ "github.com/gazeboxu/structs"
"github.com/mitchellh/mapstructure"
)
@@ -70,15 +71,22 @@ func TryInterface2Int64(data interface{}) (num int64, err error) {
if dataNumber, ok := data.(int); ok {
return int64(dataNumber), nil
}
- dataNumber, ok := data.(json.Number)
- if !ok {
- return 0, fmt.Errorf("data is not json.Number:%v to int64", data)
+ if dataNumber, ok := data.(int32); ok {
+ return int64(dataNumber), nil
}
- retVal, err := dataNumber.Int64()
- if err != nil {
- return num, err
+ if dataNumber, ok := data.(int16); ok {
+ return int64(dataNumber), nil
}
- return retVal, nil
+ if dataNumber, ok := data.(int8); ok {
+ return int64(dataNumber), nil
+ }
+ if dataNumber, ok := data.(json.Number); ok {
+ return dataNumber.Int64()
+ }
+ if str, ok := data.(string); ok {
+ return Str2Int64WithDefault(str, 0), nil
+ }
+ return 0, fmt.Errorf("data is not json.Number, it's %s, value:%v", reflect.TypeOf(data).String(), data)
}
func MustInterface2Int64(data interface{}) int64 {
@@ -114,15 +122,13 @@ func TryInterface2Float64(data interface{}) (num float64, err error) {
if dataNumber, ok := data.(float32); ok {
return float64(dataNumber), nil
}
- dataNumber, ok := data.(json.Number)
- if !ok {
- return num, fmt.Errorf("data is not json.Number:%v", data)
+ if dataNumber, ok := data.(json.Number); ok {
+ return dataNumber.Float64()
}
- retVal, err := dataNumber.Float64()
- if err != nil {
- return num, err
+ if str, ok := data.(string); ok {
+ return Str2Float64WithDefault(str, 0), nil
}
- return retVal, nil
+ return 0, fmt.Errorf("data is not json.Number, it's %s, value:%v", reflect.TypeOf(data).String(), data)
}
func MustInterface2Float64(data interface{}) float64 {
@@ -329,6 +335,10 @@ func IsTimeZero(timeValue time.Time) bool {
return timeValue == DefaultTimeValue || timeValue == ZeroTimeValue
}
+func IsPtrTimeZero(timePtr *time.Time) bool {
+ return timePtr == nil || *timePtr == DefaultTimeValue || *timePtr == ZeroTimeValue
+}
+
func HTTPBody2Values(data []byte, needDecode bool) (url.Values, error) {
bodyStr := string(data)
if needDecode {
@@ -443,36 +453,22 @@ func MergeMaps(firstMap map[string]interface{}, otherMaps ...map[string]interfac
return retVal
}
-func Struct2MapByJson(obj interface{}) (mapData map[string]interface{}) {
+func Struct2Map(obj interface{}, tagName string, isFlattenAnonymous bool) (mapData map[string]interface{}) {
+ if tagName == "" {
+ tagName = "json"
+ }
structsObj := structs.New(obj)
- structsObj.TagName = "json"
+ structsObj.TagName = tagName
+ structsObj.IsFlattenAnonymous = true
return structsObj.Map()
}
-// 此函数将MAP中所有的子MAP中的数据提升到最上层,相同字段会覆盖父MAP的
-func FlatMap(in map[string]interface{}) map[string]interface{} {
- keys := []string{}
- maps := []map[string]interface{}{}
- for k, v := range in {
- if vMap, ok := v.(map[string]interface{}); ok {
- vMap = FlatMap(vMap)
- maps = append(maps, vMap)
- keys = append(keys, k)
- }
- }
- if len(maps) > 0 {
- retVal := MergeMaps(in, maps...)
- for _, v := range keys {
- delete(retVal, v)
- }
- return retVal
- }
- return in
+func Struct2MapByJson(obj interface{}) (mapData map[string]interface{}) {
+ return Struct2Map(obj, "", false)
}
func Struct2FlatMap(obj interface{}) map[string]interface{} {
- m := Struct2MapByJson(obj)
- return FlatMap(m)
+ return Struct2Map(obj, "", true)
}
// !!! 此函数好像不支持struct是内嵌结构的
diff --git a/utils/typeconv_test.go b/utils/typeconv_test.go
index d49f2122..081503e3 100644
--- a/utils/typeconv_test.go
+++ b/utils/typeconv_test.go
@@ -84,3 +84,40 @@ func TestTime(t *testing.T) {
}
}
}
+
+func TestStruct2MapByJson(t *testing.T) {
+ type InnerKK struct {
+ IntData int
+ StrData string
+ ObjData time.Time
+ }
+ type KK struct {
+ IntData int
+ A int
+ B string
+ C time.Time
+ InnerKK
+ InnerKK2 InnerKK
+ }
+ kk := &KK{
+ InnerKK: InnerKK{
+ IntData: 1,
+ StrData: "hello",
+ },
+ }
+ mapData := Struct2MapByJson(kk)
+ t.Log(Format4Output(mapData, false))
+ // t.Log(mapData)
+ // t.Log(kk)
+}
+
+// func TestStruct2MapByJson(t *testing.T) {
+// mapData := Struct2MapByJson(&struct {
+// IntData int `structs:"dataInt"`
+// StrData string `json:"-"`
+// }{
+// IntData: 1,
+// StrData: "2",
+// })
+// t.Log(mapData)
+// }
diff --git a/utils/utils_test.go b/utils/utils_test.go
index ffdab708..79fb4b29 100644
--- a/utils/utils_test.go
+++ b/utils/utils_test.go
@@ -175,17 +175,6 @@ func TestTrimBlanChar(t *testing.T) {
}
}
-func TestStruct2MapByJson(t *testing.T) {
- mapData := Struct2MapByJson(&struct {
- IntData int `structs:"dataInt"`
- StrData string `json:"-"`
- }{
- IntData: 1,
- StrData: "2",
- })
- t.Log(mapData)
-}
-
func TestLimitUTF8StringLen(t *testing.T) {
for _, v := range [][]interface{}{
[]interface{}{
diff --git a/utils/utils_type.go b/utils/utils_type.go
new file mode 100644
index 00000000..1e9e7f91
--- /dev/null
+++ b/utils/utils_type.go
@@ -0,0 +1,69 @@
+package utils
+
+import "time"
+
+func String2Pointer(value string) *string {
+ return &value
+}
+
+func Pointer2String(ptr *string) (value string) {
+ if ptr != nil {
+ value = *ptr
+ }
+ return value
+}
+
+func Int2Pointer(value int) *int {
+ return &value
+}
+
+func Pointer2Int(ptr *int) (value int) {
+ if ptr != nil {
+ value = *ptr
+ }
+ return value
+}
+
+func Int64ToPointer(value int64) *int64 {
+ return &value
+}
+
+func Pointer2Int64(ptr *int64) (value int64) {
+ if ptr != nil {
+ value = *ptr
+ }
+ return value
+}
+
+func Float32ToPointer(value float32) *float32 {
+ return &value
+}
+
+func Pointer2Float32(ptr *float32) (value float32) {
+ if ptr != nil {
+ value = *ptr
+ }
+ return value
+}
+
+func Float64ToPointer(value float64) *float64 {
+ return &value
+}
+
+func Pointer2Float64(ptr *float64) (value float64) {
+ if ptr != nil {
+ value = *ptr
+ }
+ return value
+}
+
+func Time2Pointer(value time.Time) *time.Time {
+ return &value
+}
+
+func Pointer2Time(ptr *time.Time) (value time.Time) {
+ if ptr != nil {
+ value = *ptr
+ }
+ return value
+}