470 lines
16 KiB
Go
470 lines
16 KiB
Go
package jdapi
|
||
|
||
import (
|
||
"bytes"
|
||
"crypto/md5"
|
||
"fmt"
|
||
"io/ioutil"
|
||
"mime/multipart"
|
||
"net/http"
|
||
"net/url"
|
||
"regexp"
|
||
"strings"
|
||
|
||
"git.rosy.net.cn/baseapi"
|
||
"git.rosy.net.cn/baseapi/platformapi"
|
||
"git.rosy.net.cn/baseapi/utils"
|
||
)
|
||
|
||
const (
|
||
getStoreURL = "https://daojia.jd.com"
|
||
accessStorePageCookieName = "shop.o2o.jd.com1"
|
||
accessStorePageCookieName2 = "lsp-store1.jddj.com"
|
||
)
|
||
|
||
type SkuPageImg struct {
|
||
Big string `json:"big"`
|
||
Share string `json:"share"`
|
||
Small string `json:"small"`
|
||
}
|
||
|
||
type CorporationInfo struct {
|
||
Scope string `json:"scope"`
|
||
OperName string `json:"oper_name"`
|
||
Status string `json:"status"`
|
||
BelongOrg string `json:"belong_org"`
|
||
CreditNo string `json:"credit_no"`
|
||
RegNo string `json:"reg_no"`
|
||
ID string `json:"id"`
|
||
OrgNo string `json:"org_no"`
|
||
EconKind string `json:"econ_kind"`
|
||
EndDate string `json:"end_date"`
|
||
TermEnd string `json:"term_end"`
|
||
NeedID bool `json:"needID"`
|
||
Address string `json:"address"`
|
||
Partners []struct {
|
||
IdentifyType string `json:"identify_type"`
|
||
ShouldCapiItems []interface{} `json:"should_capi_items"`
|
||
StockType string `json:"stock_type"`
|
||
IdentifyNo string `json:"identify_no"`
|
||
RealCapiItems []interface{} `json:"real_capi_items"`
|
||
Name string `json:"name"`
|
||
} `json:"partners"`
|
||
Name string `json:"name"`
|
||
Province string `json:"province"`
|
||
TermStart string `json:"term_start"`
|
||
AbnormalItems []interface{} `json:"abnormal_items"`
|
||
CheckDate string `json:"check_date"`
|
||
RegistCapi string `json:"regist_capi"`
|
||
StartDate string `json:"start_date"`
|
||
Changerecords []struct {
|
||
BeforeContent string `json:"before_content"`
|
||
ChangeDate string `json:"change_date"`
|
||
ChangeItem string `json:"change_item"`
|
||
AfterContent string `json:"after_content"`
|
||
} `json:"changerecords"`
|
||
Branches []interface{} `json:"branches"`
|
||
Employees []struct {
|
||
JobTitle string `json:"job_title"`
|
||
Name string `json:"name"`
|
||
} `json:"employees"`
|
||
}
|
||
|
||
type PageShopInfo struct {
|
||
DisCatName string `json:"disCatName"`
|
||
DisplayType string `json:"displayType"`
|
||
FreightWords string `json:"freightWords"`
|
||
IsCart bool `json:"isCart"`
|
||
IsJb int `json:"isJb"`
|
||
IsWithHome bool `json:"isWithHome"`
|
||
LogoURL string `json:"logoUrl"`
|
||
StoreActPageList []interface{} `json:"storeActPageList"`
|
||
StoreCommentVO struct {
|
||
BuyerShowName string `json:"buyerShowName"`
|
||
CreateTime string `json:"createTime"`
|
||
IsOrgComment int `json:"isOrgComment"`
|
||
OrgCommentContent string `json:"orgCommentContent"`
|
||
Score4 int `json:"score4"`
|
||
Score4Content string `json:"score4Content"`
|
||
ScoreAvg float64 `json:"scoreAvg"`
|
||
ShowTotalCount bool `json:"showTotalCount"`
|
||
Skus []interface{} `json:"skus"`
|
||
StoreStar float64 `json:"storeStar"`
|
||
TagInfoItemList []string `json:"tagInfoItemList"`
|
||
TotalCount int `json:"totalCount"`
|
||
} `json:"storeCommentVO"`
|
||
StoreDesc string `json:"storeDesc"`
|
||
StoreInfo struct {
|
||
BackgroundType string `json:"backgroundType"`
|
||
CarrierNo int `json:"carrierNo"`
|
||
CloseStatus int `json:"closeStatus"`
|
||
DeliveryFirst string `json:"deliveryFirst"`
|
||
DeliverySecond string `json:"deliverySecond"`
|
||
ExpectArrivedTips []struct {
|
||
Msg string `json:"msg"`
|
||
Type int `json:"type"`
|
||
} `json:"expectArrivedTips"`
|
||
Flag bool `json:"flag"`
|
||
FollowNo string `json:"followNo"`
|
||
FreightTag struct {
|
||
BelongIndustry int `json:"belongIndustry"`
|
||
Type int `json:"type"`
|
||
Words string `json:"words"`
|
||
} `json:"freightTag"`
|
||
FreightWords string `json:"freightWords"`
|
||
InSaleNum string `json:"inSaleNum"`
|
||
Industry string `json:"industry"`
|
||
IsFollow bool `json:"isFollow"`
|
||
IsMembership bool `json:"isMembership"`
|
||
IsOverZone bool `json:"isOverZone"`
|
||
IsTimeFight int `json:"isTimeFight"`
|
||
LogoURL string `json:"logoUrl"`
|
||
MonthSaleNum string `json:"monthSaleNum"`
|
||
OrgCode string `json:"orgCode"`
|
||
Params string `json:"params"`
|
||
SearchLinkageFlag bool `json:"searchLinkageFlag"`
|
||
ServiceTimes []struct {
|
||
EndTime string `json:"endTime"`
|
||
StartTime string `json:"startTime"`
|
||
} `json:"serviceTimes"`
|
||
ShowType string `json:"showType"`
|
||
StationStatus int `json:"stationStatus"`
|
||
StoreAddress string `json:"storeAddress"`
|
||
StoreCertificateURL string `json:"storeCertificateUrl"`
|
||
StoreID string `json:"storeId"`
|
||
StoreName string `json:"storeName"`
|
||
StoreTel string `json:"storeTel"`
|
||
To string `json:"to"`
|
||
UpToSendprice int `json:"upToSendprice"`
|
||
} `json:"storeInfo"`
|
||
StoreShareURL string `json:"storeShareUrl"`
|
||
}
|
||
|
||
const (
|
||
LincenceEconKindPerson = "个体工商户"
|
||
|
||
QualifyTypePerson = "25" // 营业执照
|
||
QualifyTypeCompany = "22" // 身份证,个体工商户要求填
|
||
QualifyTypeAddInfo = "31" // 附加信息,如果身份证是长期有效,要求身份证背面信息
|
||
|
||
SaveQualifyActionTypeCommit = 0 // 提交
|
||
SaveQualifyActionTypeSave = 1 // 暂时保存
|
||
)
|
||
|
||
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,
|
||
}
|
||
)
|
||
|
||
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
|
||
if !isPost {
|
||
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 {
|
||
fmt.Println(k, " ", v)
|
||
w.WriteField(k, url.QueryEscape(fmt.Sprint(v)))
|
||
}
|
||
w.Close()
|
||
// b.WriteString(utils.Map2URLValues(params).Encode())
|
||
request, _ = http.NewRequest(http.MethodPost, fullURL, &b)
|
||
request.Header.Set("Content-Type", w.FormDataContentType())
|
||
}
|
||
request.Header.Set("charset", "UTF-8")
|
||
}
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
a.FillRequestCookies(request)
|
||
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")
|
||
}
|
||
retVal = jsonResult1
|
||
code, ok := jsonResult1["code"].(string)
|
||
if !ok {
|
||
return platformapi.ErrLevelGeneralFail, utils.NewErrorCode(utils.Format4Output(jsonResult1, true), "999")
|
||
}
|
||
if code == ResponseCodeSuccess {
|
||
if resultKey != "" {
|
||
retVal = jsonResult1[resultKey]
|
||
}
|
||
return platformapi.ErrLevelSuccess, nil
|
||
}
|
||
newErr := utils.NewErrorCode(jsonResult1["msg"].(string), code)
|
||
if _, ok := pageExceedLimitCodes[code]; ok {
|
||
return platformapi.ErrLevelExceedLimit, newErr
|
||
} else if _, ok := canRetryCodes[code]; ok {
|
||
return platformapi.ErrLevelRecoverableErr, newErr
|
||
} else {
|
||
baseapi.SugarLogger.Debugf("jd AccessStorePage failed, jsonResult1:%s", utils.Format4Output(jsonResult1, true))
|
||
return platformapi.ErrLevelCodeIsNotOK, newErr
|
||
}
|
||
})
|
||
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 {
|
||
return retVal["mobile"].(string), nil
|
||
}
|
||
return "", err
|
||
}
|
||
|
||
func (a *API) GetStoreOrderInfo(orderId, stationNo string) (storeOrderInfo map[string]interface{}, err error) {
|
||
params := map[string]interface{}{
|
||
"pageNo": 1,
|
||
"pageSize": 1,
|
||
"desc": true,
|
||
"param": orderId,
|
||
}
|
||
if stationNo != "" {
|
||
params["stationNo"] = stationNo
|
||
}
|
||
retVal, err := a.AccessStorePage("http://store.jddj.com/order/newManager/search", params, false)
|
||
// baseapi.SugarLogger.Debug(utils.Format4Output(retVal, false))
|
||
if err == nil {
|
||
newOrderinfoMains := retVal["newOrderinfoMains"].(map[string]interface{})
|
||
resultList := newOrderinfoMains["resultList"].([]interface{})
|
||
if len(resultList) > 0 {
|
||
return resultList[0].(map[string]interface{}), nil
|
||
}
|
||
return nil, fmt.Errorf("不能找到订单:%s的相关信息", orderId)
|
||
}
|
||
return nil, err
|
||
}
|
||
|
||
func (a *API) GetStoreOrderInfoList(fromTime, toTime string) (storeOrderList []map[string]interface{}, err error) {
|
||
pageSize := 100
|
||
pageNo := 1
|
||
params := map[string]interface{}{
|
||
"o2oOrderType": 10000,
|
||
"pageSize": pageSize,
|
||
"desc": true,
|
||
"startTimeQuery": fromTime,
|
||
"endTimeQuery": toTime,
|
||
// "stationNo": 0,
|
||
}
|
||
|
||
for {
|
||
params["pageNo"] = pageNo
|
||
retVal, err := a.AccessStorePage("http://store.jddj.com/order/newManager/tabQuery/all", params, false)
|
||
// baseapi.SugarLogger.Debug(utils.Format4Output(retVal, false))
|
||
if err == nil {
|
||
newOrderinfoMains := retVal["newOrderinfoMains"].(map[string]interface{})
|
||
resultList := newOrderinfoMains["resultList"].([]interface{})
|
||
storeOrderList = append(storeOrderList, utils.Slice2MapSlice(resultList)...)
|
||
if len(storeOrderList) >= int(utils.MustInterface2Int64(newOrderinfoMains["totalCount"])) {
|
||
return storeOrderList, nil
|
||
}
|
||
pageNo++
|
||
} else {
|
||
return nil, err
|
||
}
|
||
}
|
||
return nil, err
|
||
}
|
||
|
||
func (a *API) GetSkuPageInfo(skuId int64) (skuPageInfo map[string]interface{}, err error) {
|
||
skuIDMap := map[string]interface{}{
|
||
"skuId": utils.Int64ToStr(skuId),
|
||
"storeId": "0",
|
||
}
|
||
params := map[string]interface{}{
|
||
"platCode": "H5",
|
||
"functionId": "product/detailV6_0",
|
||
"appVersion": "6.7.0",
|
||
"body": utils.Format4Output(skuIDMap, true),
|
||
}
|
||
|
||
skuPageInfo, err = a.AccessStorePage("https://daojia.jd.com/client", params, 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)
|
||
}
|
||
return imgList, err
|
||
}
|
||
|
||
func (a *API) GetStoreInfo(storeId string) (storeInfo map[string]interface{}, err error) {
|
||
body := map[string]interface{}{
|
||
"storeId": storeId,
|
||
}
|
||
params := map[string]interface{}{
|
||
"appVersion": "6.1.0",
|
||
"functionId": "store/storeDetailV220",
|
||
"body": utils.Format4Output(body, true),
|
||
}
|
||
|
||
retVal, err := a.AccessStorePage("https://daojia.jd.com/client", params, false)
|
||
return retVal, err
|
||
}
|
||
|
||
func (a *API) GetStoreInfo2(storeID string) (storeInfo *PageShopInfo, err error) {
|
||
retVal, err := a.GetStoreInfo(storeID)
|
||
if err == nil {
|
||
err = utils.Map2StructByJson(retVal, &storeInfo, false)
|
||
}
|
||
return storeInfo, err
|
||
}
|
||
|
||
func signGetStoreList(bodyStr string) (signResult string) {
|
||
wb := "923047ae3f8d11d8b19aeb9f3d1bc200"
|
||
return fmt.Sprintf("%X", md5.Sum([]byte(bodyStr+wb)))
|
||
}
|
||
|
||
func (a *API) GetStoreList(lng string, lat string) (retVal map[string]interface{}, err error) {
|
||
body := map[string]interface{}{
|
||
"channelId": "3997",
|
||
"currentPage": 1,
|
||
"pageSize": 999,
|
||
"coordType": "2",
|
||
"platform": "1",
|
||
}
|
||
bodyStr := utils.Format4Output(body, true)
|
||
signResult := signGetStoreList(bodyStr)
|
||
params := map[string]interface{}{
|
||
"platCode": "H5",
|
||
"appVersion": "6.5.0",
|
||
"functionId": "zone/recommendStoreList",
|
||
"body": utils.Format4Output(body, true),
|
||
"signKey": signResult,
|
||
"lng": lng,
|
||
"lat": lat,
|
||
}
|
||
|
||
retVal, err = a.AccessStorePage("https://daojia.jd.com/client", params, false)
|
||
return retVal, err
|
||
}
|
||
|
||
func (a *API) GetCorporationInfo(stationNo, qualifyNumber string) (corporatonInfo *CorporationInfo, err error) {
|
||
result, err := a.AccessStorePage("https://sta-store.jddj.com/store/requestQualify.o2o", map[string]interface{}{
|
||
"stationNo": stationNo,
|
||
"qualifyNumber": qualifyNumber,
|
||
}, true)
|
||
if err == nil {
|
||
err = utils.Map2StructByJson(result, &corporatonInfo, false)
|
||
}
|
||
return corporatonInfo, err
|
||
}
|
||
|
||
func MonthSaleNum2Int(monthSaleNumStr string) (monthSaleNum int) {
|
||
searchResult := monthSaleNumReg.FindStringSubmatch(monthSaleNumStr)
|
||
if searchResult != nil && len(searchResult[1]) > 0 && len(searchResult[2]) > 0 {
|
||
monthSaleNum = int(utils.Str2Int64WithDefault(searchResult[1], 0))
|
||
if searchResult[2] == "千" {
|
||
monthSaleNum *= 1000
|
||
} else if searchResult[2] == "万" {
|
||
monthSaleNum *= 10000
|
||
}
|
||
} else {
|
||
monthSaleNum = int(utils.Str2Int64WithDefault(monthSaleNumStr, 0))
|
||
}
|
||
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
|
||
}
|