Files
baseapi/platformapi/yinbaoapi/store_page.go
邹宗楠 2ed93fe209 1
2022-10-22 22:45:36 +08:00

556 lines
21 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
}
} 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
}
type SaveProductParam struct {
CategoryUid string `json:"categoryUid"`
CategoryName string `json:"categoryName"`
Enable int `json:"enable"`
}
//更新单个商品信息(称编码和图片)
//Request URL: https://beta27.pospal.cn/Product/SaveProduct
func (a *API) SaveProduct(userId, keyword string, saveProductParam *SaveProductParam) (err error) {
var (
categoryUid, categoryName string
enable string
)
productId, err := a.LoadProductsByPage(userId, keyword)
if err != nil {
return err
}
findProductResult, err := a.FindProduct(productId)
if err != nil {
return err
}
if saveProductParam != nil {
if saveProductParam.Enable != 0 {
enable = utils.Int2Str(saveProductParam.Enable)
} else {
enable = utils.Int2Str(findProductResult.Enable)
}
if saveProductParam.CategoryUid != "" {
categoryUid = saveProductParam.CategoryUid
} else {
categoryUid = utils.Int2Str(findProductResult.Enable)
}
if saveProductParam.CategoryName != "" {
categoryName = saveProductParam.CategoryName
} else {
categoryName = utils.Int2Str(findProductResult.Enable)
}
} else {
categoryUid = utils.Int64ToStr(findProductResult.CategoryUID)
categoryName = findProductResult.Category.Name
enable = utils.Int2Str(findProductResult.Enable)
}
params2 := map[string]interface{}{
"pluCode": findProductResult.Barcode[3:],
}
params := map[string]interface{}{
"id": utils.Int2Str(findProductResult.ID),
"enable": enable,
"userId": utils.Int2Str(findProductResult.UserID),
"barcode": findProductResult.Barcode,
"name": findProductResult.Name,
"categoryUid": categoryUid,
"categoryName": categoryName,
"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/Product/ResetCoverImage
func (a *API) ResetCoverImage(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
}
if len(findProductResult.Productimages) > 0 {
_, err = a.AccessStorePage("Product/ResetCoverImage", map[string]interface{}{
"productImageId": findProductResult.Productimages[len(findProductResult.Productimages)-1].ID,
"forMulColorSize": false,
})
}
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
}
//银豹改变商品状态?
//https://beta27.pospal.cn/Product/BatchUpdateProductEnable
func (a *API) BatchUpdateProductEnable(userID, productId string, enable int) (err error) {
_, err = a.AccessStorePage("Product/BatchUpdateProductEnable", map[string]interface{}{
"enable": enable,
"productRange[userId]": userID,
"productRange[enable]": enable,
"productRange[isProductRequestedStore]": false,
"productRange[isAddvancedSearch]": false,
"productRange[isUpdateAllProduct]": false,
"productRange[productUidsJson]": []string{productId},
})
return err
}