491 lines
19 KiB
Go
491 lines
19 KiB
Go
package yinbaoapi
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/json"
|
||
"fmt"
|
||
"math/rand"
|
||
"mime/multipart"
|
||
"net/http"
|
||
"net/textproto"
|
||
"regexp"
|
||
"strings"
|
||
|
||
"git.rosy.net.cn/baseapi"
|
||
"git.rosy.net.cn/baseapi/platformapi"
|
||
"git.rosy.net.cn/baseapi/utils"
|
||
)
|
||
|
||
const (
|
||
pageUrl = "https://beta27.pospal.cn"
|
||
MainStoreVendorOrgCode = "3933189"
|
||
letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||
)
|
||
|
||
var (
|
||
regexpSkuID = regexp.MustCompile(`<tr data="(.*?)"`)
|
||
)
|
||
|
||
func (a *API) AccessStorePage(action string, bizParams map[string]interface{}) (retVal map[string]interface{}, err error) {
|
||
fullURL := utils.GenerateGetURL(pageUrl, action, nil)
|
||
// result, _ := json.MarshalIndent(bizParams, "", " ")
|
||
err = platformapi.AccessPlatformAPIWithRetry(a.client,
|
||
func() *http.Request {
|
||
request, _ := http.NewRequest(http.MethodPost, fullURL, strings.NewReader(utils.Map2URLValues(bizParams).Encode()))
|
||
request.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
|
||
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")
|
||
}
|
||
if err == nil {
|
||
if jsonResult1["successed"] != nil {
|
||
if !jsonResult1["successed"].(bool) {
|
||
errLevel = platformapi.ErrLevelGeneralFail
|
||
err = utils.NewErrorCode(jsonResult1["msg"].(string), "-1", 0)
|
||
baseapi.SugarLogger.Debugf("yinbao AccessStorePageAPI failed, jsonResult1:%s", utils.Format4Output(jsonResult1, true))
|
||
}
|
||
} else {
|
||
errLevel = platformapi.ErrLevelGeneralFail
|
||
if strings.Contains(jsonResult1["fakeData"].(string), "登录") {
|
||
err = utils.NewErrorCode("银豹Cookie可能失效了!", "-1", 0)
|
||
}
|
||
}
|
||
retVal = jsonResult1
|
||
}
|
||
return errLevel, err
|
||
})
|
||
return retVal, err
|
||
}
|
||
|
||
func RandStringBytes(n int) string {
|
||
b := make([]byte, n)
|
||
for i := range b {
|
||
b[i] = letterBytes[rand.Intn(len(letterBytes))]
|
||
}
|
||
return string(b)
|
||
}
|
||
|
||
func getBoundary() (boundary string) {
|
||
boundary = "----WebKitFormBoundary"
|
||
boundary += RandStringBytes(16)
|
||
return boundary
|
||
}
|
||
|
||
func (a *API) AccessStorePage2(action string, userID, productID string, data []byte, fileName string) (retVal map[string]interface{}, err error) {
|
||
// 实例化multipart
|
||
body := &bytes.Buffer{}
|
||
writer := multipart.NewWriter(body)
|
||
writer.SetBoundary(getBoundary())
|
||
// 创建multipart 文件字段
|
||
h := make(textproto.MIMEHeader)
|
||
h.Set("Content-Disposition",
|
||
fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
|
||
"file", fileName))
|
||
h.Set("Content-Type", "image/jpeg")
|
||
part, _ := writer.CreatePart(h)
|
||
// part, _ := writer.CreateFormFile("file", "4ff17b24fdccfc37fd0847469a8039e9.jpg")
|
||
// 写入文件数据到multipart,和读取本地文件方法的唯一区别
|
||
_, err = part.Write(data)
|
||
//将额外参数也写入到multipart
|
||
_ = writer.WriteField("name", fileName)
|
||
err = writer.Close()
|
||
fullURL := utils.GenerateGetURL(pageUrl, action, nil)
|
||
err = platformapi.AccessPlatformAPIWithRetry(a.client,
|
||
func() *http.Request {
|
||
request, _ := http.NewRequest(http.MethodPost, fullURL+"?userId="+userID+"&productId="+productID, body)
|
||
request.Header.Set("Content-Type", writer.FormDataContentType())
|
||
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")
|
||
}
|
||
if err == nil {
|
||
if jsonResult1["successed"] != nil {
|
||
if !jsonResult1["successed"].(bool) {
|
||
errLevel = platformapi.ErrLevelGeneralFail
|
||
err = utils.NewErrorCode(jsonResult1["msg"].(string), "-1", 0)
|
||
baseapi.SugarLogger.Debugf("yinbao AccessStorePageAPI failed, jsonResult1:%s", utils.Format4Output(jsonResult1, true))
|
||
}
|
||
} else {
|
||
errLevel = platformapi.ErrLevelGeneralFail
|
||
if strings.Contains(jsonResult1["fakeData"].(string), "登录") {
|
||
err = utils.NewErrorCode("银豹Cookie可能失效了!", "-1", 0)
|
||
}
|
||
}
|
||
retVal = jsonResult1
|
||
}
|
||
return errLevel, err
|
||
})
|
||
return retVal, err
|
||
}
|
||
|
||
func IsErrCookie(err error) (isExist bool) {
|
||
return utils.IsErrMatch(err, "-1", []string{"银豹Cookie可能失效了"})
|
||
}
|
||
|
||
type LoadSubStoresByUserIdDDLJsonResult struct {
|
||
Balance float64 `json:"balance"`
|
||
Storewebsite interface{} `json:"storewebsite"`
|
||
Version interface{} `json:"version"`
|
||
IsParent int `json:"isParent"`
|
||
SortNo int `json:"sortNo"`
|
||
UserLocation interface{} `json:"userLocation"`
|
||
StoreDomain interface{} `json:"storeDomain"`
|
||
ParentAccount interface{} `json:"parentAccount"`
|
||
ParentUserID interface{} `json:"parentUserId"`
|
||
AreaID string `json:"areaId"`
|
||
SubUsers interface{} `json:"subUsers"`
|
||
CamerasSettings interface{} `json:"camerasSettings"`
|
||
ImageRecognitionRemainingTimes string `json:"imageRecognitionRemainingTimes"`
|
||
CrmVipNum interface{} `json:"crmVipNum"`
|
||
ID int `json:"id"`
|
||
Account string `json:"account"`
|
||
Email interface{} `json:"email"`
|
||
Address string `json:"address"`
|
||
Tel string `json:"tel"`
|
||
Company string `json:"company"`
|
||
Industry string `json:"industry"`
|
||
IP interface{} `json:"ip"`
|
||
Vip interface{} `json:"vip"`
|
||
CreatedDatetime interface{} `json:"createdDatetime"`
|
||
Enable interface{} `json:"enable"`
|
||
IsOldRev interface{} `json:"isOldRev"`
|
||
EnterpriseID interface{} `json:"enterpriseId"`
|
||
Source interface{} `json:"source"`
|
||
AppID interface{} `json:"appId"`
|
||
AppKey interface{} `json:"appKey"`
|
||
UserType int `json:"userType"`
|
||
IsFranchise int `json:"isFranchise"`
|
||
SecondIndustry interface{} `json:"secondIndustry"`
|
||
Number interface{} `json:"number"`
|
||
TimeZoneID interface{} `json:"timeZoneId"`
|
||
}
|
||
|
||
//获取所有门店的userID
|
||
//https://beta27.pospal.cn/Account/LoadSubStoresByUserIdDDLJson
|
||
func (a *API) LoadSubStoresByUserIdDDLJson() (results []*LoadSubStoresByUserIdDDLJsonResult, err error) {
|
||
result, err := a.AccessStorePage("Account/LoadSubStoresByUserIdDDLJson", map[string]interface{}{
|
||
"userId": MainStoreVendorOrgCode,
|
||
"withSelf": false,
|
||
})
|
||
if err == nil {
|
||
utils.Map2StructByJson(result["stores"], &results, false)
|
||
}
|
||
return results, err
|
||
}
|
||
|
||
//获得分类ID
|
||
//https://beta27.pospal.cn/Category/CreateCategoryUid
|
||
func (a *API) CreateCategoryUid() (categoryID string, err error) {
|
||
result, err := a.AccessStorePage("Category/CreateCategoryUid", nil)
|
||
if err == nil {
|
||
categoryID = result["uid"].(string)
|
||
}
|
||
return categoryID, err
|
||
}
|
||
|
||
//添加分类
|
||
//经测试,parentCategoryName为空或者平台上不存在都是默认建成1级分类
|
||
//https://beta27.pospal.cn/Category/AddNewCategory
|
||
func (a *API) AddNewCategory(userId, categoryName, parentCategoryName string) (catID string, err error) {
|
||
uid, err := a.CreateCategoryUid()
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
_, err = a.AccessStorePage("Category/AddNewCategory", map[string]interface{}{
|
||
"userId": userId,
|
||
"uid": uid,
|
||
"parentCategoryName": parentCategoryName,
|
||
"categoryName": categoryName,
|
||
"categoryType": 0,
|
||
})
|
||
return uid, err
|
||
}
|
||
|
||
func IsErrCategoryExist(err error) (isExist bool) {
|
||
return utils.IsErrMatch(err, "", []string{"您输入的商品分类名称已存在"})
|
||
}
|
||
|
||
//修改分类
|
||
//经测试,如果parentCategoryName为空或在平台上不存在,则此分类会单独从之前的分类中移出重建成一级分类,0_0||
|
||
//https://beta27.pospal.cn/Category/Update
|
||
func (a *API) UpdateCategory(userId, categoryUid, newCategoryName, parentCategoryName string) (err error) {
|
||
_, err = a.AccessStorePage("Category/Update", map[string]interface{}{
|
||
"userId": userId,
|
||
"categoryUid": categoryUid,
|
||
"parentCategoryName": parentCategoryName,
|
||
"newCategoryName": newCategoryName,
|
||
})
|
||
return err
|
||
}
|
||
|
||
//删除分类
|
||
// https://beta27.pospal.cn/Category/Delete
|
||
func (a *API) DeleteCategory(userId string, categoryUidsJson []string) (err error) {
|
||
_, err = a.AccessStorePage("Category/Delete", map[string]interface{}{
|
||
"userId": userId,
|
||
"categoryUidsJson": categoryUidsJson,
|
||
})
|
||
return err
|
||
}
|
||
|
||
type LoadCategorysWithOptionResult struct {
|
||
ID int `json:"id"`
|
||
UserID int `json:"userId"`
|
||
UID int64 `json:"uid"`
|
||
ParentUID int `json:"parentUid"`
|
||
Name string `json:"name"`
|
||
Enable int `json:"enable"`
|
||
CreatedDatetime string `json:"createdDatetime"`
|
||
UpdatedDatetime string `json:"updatedDatetime"`
|
||
CategoryType int `json:"categoryType"`
|
||
Categoryoption struct {
|
||
ID int `json:"id"`
|
||
UserID int `json:"userId"`
|
||
CategoryOrder int `json:"categoryOrder"`
|
||
CategoryUID int64 `json:"categoryUid"`
|
||
HideFromClient int `json:"hideFromClient"`
|
||
} `json:"categoryoption"`
|
||
TxtUID string `json:"txtUid"`
|
||
TxtParentUID string `json:"txtParentUid"`
|
||
}
|
||
|
||
//获取所有分类
|
||
//https://beta27.pospal.cn/Category/LoadCategorysWithOption
|
||
func (a *API) LoadCategorysWithOption(userId string) (loadCategorysWithOptionResult []*LoadCategorysWithOptionResult, err error) {
|
||
result, err := a.AccessStorePage("Category/LoadCategorysWithOption", map[string]interface{}{
|
||
"userId": userId,
|
||
})
|
||
if err == nil {
|
||
utils.Map2StructByJson(result["categorys"], &loadCategorysWithOptionResult, false)
|
||
}
|
||
return loadCategorysWithOptionResult, err
|
||
}
|
||
|
||
//获取单个商品id
|
||
//https://beta27.pospal.cn/Product/LoadProductsByPage
|
||
func (a *API) LoadProductsByPage(userId, keyword string) (v string, err error) {
|
||
result, err := a.AccessStorePage("Product/LoadProductsByPage", map[string]interface{}{
|
||
"userId": userId,
|
||
"keyword": keyword,
|
||
"pageIndex": 1,
|
||
"pageSize": 50,
|
||
"enable": 1,
|
||
})
|
||
if err == nil {
|
||
body := result["contentView"].(string)
|
||
if strings.Contains(body, "未查询到符合条件的记录") {
|
||
err = utils.NewErrorCode("未查询到符合条件的记录!", "-1", 0)
|
||
return "", err
|
||
}
|
||
result := regexpSkuID.FindStringSubmatch(body)
|
||
return result[1], err
|
||
}
|
||
return "", err
|
||
}
|
||
|
||
type FindProductResult struct {
|
||
Category struct {
|
||
ID int `json:"id"`
|
||
UserID int `json:"userId"`
|
||
UID int64 `json:"uid"`
|
||
ParentUID int64 `json:"parentUid"`
|
||
Name string `json:"name"`
|
||
Enable int `json:"enable"`
|
||
CreatedDatetime string `json:"createdDatetime"`
|
||
UpdatedDatetime string `json:"updatedDatetime"`
|
||
CategoryType int `json:"categoryType"`
|
||
TxtUID string `json:"txtUid"`
|
||
} `json:"category"`
|
||
Productimages []struct {
|
||
Barcode string `json:"barcode"`
|
||
CreatedDatetime string `json:"createdDatetime"`
|
||
ID int `json:"id"`
|
||
IsCover bool `json:"isCover"`
|
||
Path string `json:"path"`
|
||
ProductName string `json:"productName"`
|
||
ProductUID int64 `json:"productUid"`
|
||
UID int64 `json:"uid"`
|
||
UpdatedDatetime string `json:"updatedDatetime"`
|
||
UserID int `json:"userId"`
|
||
} `json:"productimages"`
|
||
CategoryShowID int `json:"categoryShowId"`
|
||
ParentHas int `json:"parentHas"`
|
||
TxtUID string `json:"txtUid"`
|
||
UpdateStock float64 `json:"updateStock"`
|
||
IsOutOfStock bool `json:"isOutOfStock"`
|
||
ProductUnitExchangeList []interface{} `json:"productUnitExchangeList"`
|
||
BaseUnitName string `json:"baseUnitName"`
|
||
IsCurrentPrice bool `json:"isCurrentPrice"`
|
||
DisableEntireDiscount bool `json:"disableEntireDiscount"`
|
||
IsPrior bool `json:"isPrior"`
|
||
ProductTags []interface{} `json:"productTags"`
|
||
CustomerPrices []interface{} `json:"customerPrices"`
|
||
EnableBatch int `json:"enableBatch"`
|
||
HideFromClient int `json:"hideFromClient"`
|
||
SpecNum int `json:"specNum"`
|
||
IsEnableVirtualStock bool `json:"isEnableVirtualStock"`
|
||
IsPassPromotionProduct bool `json:"isPassPromotionProduct"`
|
||
IsBindPassProduct bool `json:"isBindPassProduct"`
|
||
IgnoreStock bool `json:"ignoreStock"`
|
||
DataIndex int `json:"dataIndex"`
|
||
SellPriceIsNull bool `json:"sellPriceIsNull"`
|
||
Spu string `json:"spu"`
|
||
ID int `json:"id"`
|
||
UID int64 `json:"uid"`
|
||
CategoryUID int64 `json:"categoryUid"`
|
||
UserID int `json:"userId"`
|
||
Name string `json:"name"`
|
||
Barcode string `json:"barcode"`
|
||
BuyPrice float64 `json:"buyPrice"`
|
||
SellPrice float64 `json:"sellPrice"`
|
||
Stock float64 `json:"stock"`
|
||
MaxStock float64 `json:"maxStock"`
|
||
MinStock float64 `json:"minStock"`
|
||
Pinyin string `json:"pinyin"`
|
||
SellPrice2 float64 `json:"sellPrice2"`
|
||
IsPoint int `json:"isPoint"`
|
||
Enable int `json:"enable"`
|
||
CreatedDatetime string `json:"createdDatetime"`
|
||
ProductTastes []interface{} `json:"productTastes"`
|
||
}
|
||
|
||
//获取单个商品信息
|
||
//https://beta27.pospal.cn/Product/FindProduct
|
||
func (a *API) FindProduct(productId string) (findProductResult *FindProductResult, err error) {
|
||
result, err := a.AccessStorePage("Product/FindProduct", map[string]interface{}{
|
||
"productId": productId,
|
||
})
|
||
if err == nil {
|
||
utils.Map2StructByJson(result["product"], &findProductResult, false)
|
||
}
|
||
return findProductResult, err
|
||
}
|
||
|
||
//更新单个商品信息(称编码和图片)
|
||
//Request URL: https://beta27.pospal.cn/Product/SaveProduct
|
||
func (a *API) SaveProduct(userId, keyword string) (err error) {
|
||
productId, err := a.LoadProductsByPage(userId, keyword)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
findProductResult, err := a.FindProduct(productId)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
params2 := map[string]interface{}{
|
||
"pluCode": findProductResult.Barcode[3:],
|
||
}
|
||
params := map[string]interface{}{
|
||
"id": utils.Int2Str(findProductResult.ID),
|
||
"enable": utils.Int2Str(findProductResult.Enable),
|
||
"userId": utils.Int2Str(findProductResult.UserID),
|
||
"barcode": findProductResult.Barcode,
|
||
"name": findProductResult.Name,
|
||
"categoryUid": utils.Int64ToStr(findProductResult.CategoryUID),
|
||
"categoryName": findProductResult.Category.Name,
|
||
"sellPrice": utils.Float64ToStr(findProductResult.SellPrice),
|
||
"buyPrice": utils.Float64ToStr(findProductResult.BuyPrice),
|
||
"isCustomerDiscount": "1",
|
||
"customerPrice": "",
|
||
"sellPrice2": utils.Float64ToStr(findProductResult.SellPrice2),
|
||
"pinyin": findProductResult.Pinyin,
|
||
"supplierUid": "",
|
||
"supplierName": "无",
|
||
"productionDate": "",
|
||
"shelfLife": "",
|
||
"maxStock": utils.Float64ToStr(findProductResult.MaxStock),
|
||
"minStock": utils.Float64ToStr(findProductResult.MinStock),
|
||
"stock": utils.Float64ToStr(findProductResult.Stock),
|
||
"description": "",
|
||
"noStock": "0",
|
||
"productCommonAttribute": params2,
|
||
"baseUnitName": findProductResult.BaseUnitName,
|
||
"customerPrices": []string{},
|
||
"productUnitExchangeList": []string{},
|
||
"productTags": []string{},
|
||
"productTastes": []string{},
|
||
"attribute1": "",
|
||
"attribute2": "",
|
||
"attribute3": "",
|
||
"attribute4": "",
|
||
"attribute6": "",
|
||
}
|
||
result, _ := json.Marshal(params)
|
||
_, err = a.AccessStorePage("Product/SaveProduct", map[string]interface{}{
|
||
"productJson": string(result),
|
||
})
|
||
return err
|
||
}
|
||
|
||
//上传图片
|
||
//https://beta27.pospal.cn/Product/UploadProductImage?userId=3933189&productId=8788044
|
||
func (a *API) UploadProductImage(userID, productId string, data []byte, fileName string) (err error) {
|
||
_, err = a.AccessStorePage2("Product/UploadProductImage", userID, productId, data, fileName)
|
||
return err
|
||
}
|
||
|
||
//登录?
|
||
//https://beta27.pospal.cn/account/SignIn?noLog=
|
||
//userName: 18048531223
|
||
// password: Rosy201507
|
||
// returnUrl:
|
||
// screenSize: 1600*900
|
||
//Accept: application/json, text/javascript, */*; q=0.01
|
||
//Accept-Encoding: gzip, deflate, br
|
||
//Accept-Language: zh-CN,zh;q=0.9
|
||
//Connection: keep-alive
|
||
//Content-Length: 71
|
||
//Content-Type: application/x-www-form-urlencoded; charset=UTF-8
|
||
//Host: beta27.pospal.cn
|
||
//Origin: https://beta27.pospal.cn
|
||
//Referer: https://beta27.pospal.cn/account/signin
|
||
//Sec-Fetch-Mode: cors
|
||
//Sec-Fetch-Site: same-origin
|
||
//User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36
|
||
//X-Requested-With: XMLHttpRequest
|
||
func (a *API) TryGetCookie() (cookie string, err error) {
|
||
params := map[string]interface{}{
|
||
"userName": 18048531223,
|
||
"password": "Rosy201507",
|
||
"returnUrl": "",
|
||
"screenSize": "1600*900",
|
||
}
|
||
err = platformapi.AccessPlatformAPIWithRetry(a.client,
|
||
func() *http.Request {
|
||
request, _ := http.NewRequest(http.MethodPost, "https://beta27.pospal.cn/account/SignIn?noLog=", strings.NewReader(utils.Map2URLValues(params).Encode()))
|
||
request.Header.Set("Accept", "application/json, text/javascript, */*; q=0.01")
|
||
request.Header.Set("Accept-Encoding", "gzip, deflate, br")
|
||
request.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
|
||
request.Header.Set("Connection", "keep-alive")
|
||
request.Header.Set("Content-Length", "56")
|
||
request.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
|
||
request.Header.Set("Host", "beta27.pospal.cn")
|
||
request.Header.Set("Origin", "https://beta27.pospal.cn")
|
||
request.Header.Set("Referer", "https://beta27.pospal.cn/account/signin")
|
||
request.Header.Set("Sec-Fetch-Mode", "cors")
|
||
request.Header.Set("Sec-Fetch-Site", "same-origin")
|
||
request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36")
|
||
request.Header.Set("X-Requested-With", "XMLHttpRequest")
|
||
return request
|
||
},
|
||
a.config,
|
||
func(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) {
|
||
cookies := response.Cookies()
|
||
for _, v := range cookies {
|
||
if v.Name == ".POSPALAUTH30220" && v.Value != "" {
|
||
cookie = v.Value
|
||
}
|
||
}
|
||
return errLevel, err
|
||
})
|
||
return cookie, err
|
||
}
|