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 ( QualifyTypeCompany = "25" // 营业执照 QualifyTypePerson = "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, } 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 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 := pageCanRetryCodes[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 }