Files
jx-callback/business/partner/purchase/ebai/store_sku2.go
2020-01-16 10:47:42 +08:00

495 lines
19 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 ebai
import (
"regexp"
"time"
"git.rosy.net.cn/baseapi/platformapi/ebaiapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/business/partner/putils"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
const (
defVendorCatID = 201222934 // 其他蔬菜
)
var (
sensitiveWordRegexp = regexp.MustCompile(`商品名称中含有敏感词(\[.*\])`)
)
func (p *PurchaseHandler) GetStoreSkusBatchSize(funcID int) (batchSize int) {
switch funcID {
case partner.FuncUpdateStoreSkusStock, partner.FuncUpdateStoreSkusStatus, partner.FuncUpdateStoreSkusPrice, partner.FuncDeleteStoreSkus:
batchSize = ebaiapi.MaxStoreSkuBatchSize
case partner.FuncCreateStoreSkus, partner.FuncUpdateStoreSkus:
batchSize = 1
case partner.FuncGetStoreSkusFullInfo:
batchSize = 1
case partner.FuncCreateActs, partner.FuncCancelActs:
batchSize = 1
}
return batchSize
}
// 门店分类
func (p *PurchaseHandler) GetStoreAllCategories(ctx *jxcontext.Context, storeID int, vendorStoreID string) (cats []*partner.BareCategoryInfo, err error) {
remoteCats, err := api.EbaiAPI.ShopCategoryGet(utils.Int2Str(storeID))
if err == nil {
cats = convertVendorCatList(remoteCats)
}
return cats, err
}
func convertVendorCatList(remoteCats []*ebaiapi.CategoryInfo) (cats []*partner.BareCategoryInfo) {
for _, rCat := range remoteCats {
cat := &partner.BareCategoryInfo{
VendorCatID: utils.Int64ToStr(rCat.CategoryID),
Name: rCat.Name,
Level: rCat.Level,
Seq: jxCatSeq2Ebai(rCat.Rank),
Children: convertVendorCatList(rCat.Children),
}
cats = append(cats, cat)
}
return cats
}
func (p *PurchaseHandler) IsErrCategoryExist(err error) (isExist bool) {
return ebaiapi.IsErrCategoryExist(err)
}
func (p *PurchaseHandler) IsErrCategoryNotExist(err error) (isNotExist bool) {
return ebaiapi.IsErrCategoryNotExist(err)
}
func (p *PurchaseHandler) CreateStoreCategory(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeCat *dao.SkuStoreCatInfo) (err error) {
var vendorCatID int64
if globals.EnableEbaiStoreWrite {
vendorCatID, err = api.EbaiAPI.ShopCategoryCreate(utils.Int2Str(storeID), utils.Str2Int64WithDefault(storeCat.ParentVendorCatID, 0), formatCatName(storeCat.Name), jxCatSeq2Ebai(storeCat.Seq))
} else {
vendorCatID = jxutils.GenFakeID()
}
storeCat.VendorCatID = utils.Int64ToStr(vendorCatID)
return err
}
func (p *PurchaseHandler) UpdateStoreCategory(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeCat *dao.SkuStoreCatInfo) (err error) {
if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.ShopCategoryUpdate(utils.Int2Str(storeID), utils.Str2Int64WithDefault(storeCat.VendorCatID, 0), formatCatName(storeCat.Name), jxCatSeq2Ebai(storeCat.Seq))
// todo, 饿百将一个分类重复改名也会报分类名重复错特殊处理一下不过因为GetStoreCategory其实会拉取所有的门店分类是比较耗时的操作
if utils.IsErrMatch(err, "1", []string{"分类名称已经存在"}) {
if cat, err2 := p.GetStoreCategory(ctx, storeID, vendorStoreID, storeCat.Name); err2 == nil {
if cat.VendorCatID == storeCat.VendorCatID {
err = nil
}
}
}
}
return err
}
func (p *PurchaseHandler) DeleteStoreCategory(ctx *jxcontext.Context, storeID int, vendorStoreID, vendorCatID string, level int) (err error) {
if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.ShopCategoryDelete(utils.Int2Str(storeID), utils.Str2Int64WithDefault(vendorCatID, 0))
}
return err
}
// 门店商品
// 多门店平台不需要实现这个接口
func (p *PurchaseHandler) IsErrSkuExist(err error) (isExist bool) {
return ebaiapi.IsErrSkuExist(err)
}
func (p *PurchaseHandler) IsErrSkuNotExist(err error) (isNotExist bool) {
return ebaiapi.IsErrSkuNotExist(err)
}
func (p *PurchaseHandler) updateStoreSkus(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*dao.StoreSkuSyncInfo, isNeedMapCat bool) (failedList []*partner.StoreSkuInfoWithErr, err error) {
storeSku := storeSkuList[0]
strStoreID := utils.Int2Str(storeID)
params := genSkuParamsFromStoreSkuInfo2(storeSku, false)
if globals.EnableEbaiStoreWrite {
_, err = api.EbaiAPI.SkuUpdate(ctx.GetTrackInfo(), strStoreID, utils.Str2Int64(storeSku.VendorSkuID), params)
if err != nil {
failedList = putils.GetErrMsg2FailedSingleList(storeSkuList, err, storeID, model.VendorIDEBAI, "更新商品基础信息")
}
if isNeedMapCat {
utils.CallFuncAsync(func() {
api.EbaiAPI.SkuShopCategoryMap(strStoreID, utils.Str2Int64(storeSku.VendorSkuID), "", utils.Str2Int64(storeSku.VendorCatID), genSkuCatRank(storeSku))
})
}
}
return failedList, err
}
func (p *PurchaseHandler) UpdateStoreSkus(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*dao.StoreSkuSyncInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) {
return p.updateStoreSkus(ctx, storeID, vendorStoreID, storeSkuList, true)
}
// 对于多门店平台来说storeSkuList中只有SkuID与VendorSkuID有意义
func (p *PurchaseHandler) CreateStoreSkus(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*dao.StoreSkuSyncInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) {
storeSku := storeSkuList[0]
var vendorSkuID int64
params := genSkuParamsFromStoreSkuInfo2(storeSku, true)
if globals.EnableEbaiStoreWrite {
strStoreID := utils.Int2Str(storeID)
if vendorSkuID, err = api.EbaiAPI.SkuCreate(ctx.GetTrackInfo(), strStoreID, storeSku.SkuID, params); err == nil {
utils.AfterFuncWithRecover(5*time.Second, func() {
api.EbaiAPI.SkuShopCategoryMap(strStoreID, vendorSkuID, "", utils.Str2Int64(storeSku.VendorCatID), genSkuCatRank(storeSku))
// 饿百平台有BUG会导致新建一个之前删除的商品时信息不会及时更新强制刷新一下
// 比如门店100887, skuID33805订单1577176719141226065
p.updateStoreSkus(ctx, storeID, vendorStoreID, storeSkuList, false)
})
} else {
failedList = putils.GetErrMsg2FailedSingleList(storeSkuList, err, storeID, model.VendorIDEBAI, "创建商品")
}
} else {
vendorSkuID = jxutils.GenFakeID()
}
storeSku.VendorSkuID = utils.Int64ToStr(vendorSkuID)
return failedList, err
}
func getFailedVendorSkuIDsFromOpResult(opResult *ebaiapi.BatchOpResult) (skuIDs []string) {
if opResult != nil {
for _, v := range opResult.FailedList {
skuIDs = append(skuIDs, utils.Int64ToStr(v.SkuID))
}
}
return skuIDs
}
func (p *PurchaseHandler) DeleteStoreSkus(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) {
if globals.EnableEbaiStoreWrite {
opResult, err2 := api.EbaiAPI.SkuDelete(ctx.GetTrackInfo(), utils.Int2Str(storeID), partner.BareStoreSkuInfoList(storeSkuList).GetVendorSkuIDIntList(), nil)
if err = err2; err2 != nil && opResult != nil {
// if len(storeSkuList) > len(opResult.FailedList) {
failedList = SelectStoreSkuListByOpResult(storeSkuList, opResult, storeID, model.VendorIDEBAI, "删除商品")
// successList = putils.UnselectStoreSkuListByVendorSkuIDs(storeSkuList, getFailedVendorSkuIDsFromOpResult(opResult))
// }
}
}
return failedList, err
}
func (p *PurchaseHandler) UpdateStoreSkusStatus(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo, status int) (failedList []*partner.StoreSkuInfoWithErr, err error) {
vendorSkuIDs := partner.BareStoreSkuInfoList(storeSkuList).GetVendorSkuIDIntList()
if globals.EnableEbaiStoreWrite {
var opResult *ebaiapi.BatchOpResult
if status == model.SkuStatusNormal {
if len(vendorSkuIDs) > 1 {
opResult, err = api.EbaiAPI.SkuOnline(ctx.GetTrackInfo(), utils.Int2Str(storeID), vendorSkuIDs, nil, nil)
} else if len(vendorSkuIDs) == 1 {
err = api.EbaiAPI.SkuOnlineOne(ctx.GetTrackInfo(), utils.Int2Str(storeID), vendorSkuIDs[0], "", "")
if err != nil {
failedList = putils.GetErrMsg2FailedSingleList(storeSkuList, err, storeID, model.VendorIDEBAI, "更新商品状态")
}
return failedList, err
}
} else {
if len(vendorSkuIDs) > 1 {
opResult, err = api.EbaiAPI.SkuOffline(ctx.GetTrackInfo(), utils.Int2Str(storeID), vendorSkuIDs, nil, nil)
} else if len(vendorSkuIDs) == 1 {
err = api.EbaiAPI.SkuOfflineOne(ctx.GetTrackInfo(), utils.Int2Str(storeID), vendorSkuIDs[0], "", "")
if err != nil {
failedList = putils.GetErrMsg2FailedSingleList(storeSkuList, err, storeID, model.VendorIDEBAI, "更新商品状态")
}
return failedList, err
}
}
if err != nil && opResult != nil {
failedList = SelectStoreSkuListByOpResult(storeSkuList, opResult, storeID, model.VendorIDEBAI, "更新商品状态")
// failedList = putils.UnselectStoreSkuListByVendorSkuIDs(storeSkuList, getFailedVendorSkuIDsFromOpResult(opResult))
}
}
return failedList, err
}
func StoreSkuInfoList2Ebai(storeSkuList []*partner.StoreSkuInfo) (outList ebaiapi.ShopSkuInfoList) {
outList = make(ebaiapi.ShopSkuInfoList, len(storeSkuList))
for k, v := range storeSkuList {
outList[k] = &ebaiapi.ShopSkuInfo{
SkuID: utils.Str2Int64WithDefault(v.VendorSkuID, 0),
// CustomSkuID: utils.Int2Str(v.SkuID),
SalePrice: v.VendorPrice,
Stock: v.Stock,
}
}
return outList
}
func (p *PurchaseHandler) UpdateStoreSkusPrice(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) {
if globals.EnableEbaiStoreWrite {
if len(storeSkuList) > 1 {
opResult, err2 := api.EbaiAPI.SkuPriceUpdateBatch(ctx.GetTrackInfo(), utils.Int2Str(storeID), StoreSkuInfoList2Ebai(storeSkuList), ebaiapi.SkuIDTypeSkuID)
if err = err2; err != nil && opResult != nil {
failedList = SelectStoreSkuListByOpResult(storeSkuList, opResult, storeID, model.VendorIDEBAI, "更新商品价格")
}
} else if len(storeSkuList) == 1 {
_, err := api.EbaiAPI.SkuPriceUpdateOne(ctx.GetTrackInfo(), utils.Int2Str(storeID), StoreSkuInfoList2Ebai(storeSkuList)[0])
if err != nil {
failedList = putils.GetErrMsg2FailedSingleList(storeSkuList, err, storeID, model.VendorIDEBAI, "更新商品价格")
}
}
}
return failedList, err
}
func (p *PurchaseHandler) UpdateStoreSkusStock(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) {
if globals.EnableEbaiStoreWrite {
if len(storeSkuList) > 1 {
opResult, err2 := api.EbaiAPI.SkuStockUpdateBatch(ctx.GetTrackInfo(), utils.Int2Str(storeID), StoreSkuInfoList2Ebai(storeSkuList), ebaiapi.SkuIDTypeSkuID)
if err = err2; err != nil && opResult != nil {
failedList = SelectStoreSkuListByOpResult(storeSkuList, opResult, storeID, model.VendorIDEBAI, "更新商品库存")
// successList = putils.UnselectStoreSkuListByVendorSkuIDs(storeSkuList, getFailedVendorSkuIDsFromOpResult(opResult))
}
} else if len(storeSkuList) == 1 {
err = api.EbaiAPI.SkuStockUpdateOne(ctx.GetTrackInfo(), utils.Int2Str(storeID), StoreSkuInfoList2Ebai(storeSkuList)[0])
if err != nil {
failedList = putils.GetErrMsg2FailedSingleList(storeSkuList, err, storeID, model.VendorIDEBAI, "更新商品库存")
}
}
}
return failedList, err
}
func genSkuParamsFromStoreSkuInfo2(storeSku *dao.StoreSkuSyncInfo, isCreate bool) (params map[string]interface{}) {
photos := []map[string]interface{}{
map[string]interface{}{
"is_master": true,
"url": storeSku.Img,
},
}
if storeSku.Img2 != "" {
photos = append(photos, map[string]interface{}{
"is_master": false,
"url": storeSku.Img2,
})
}
params = map[string]interface{}{
"name": utils.LimitMixedStringLen(storeSku.SkuName, ebaiapi.MaxSkuNameByteCount),
"left_num": model.MaxStoreSkuStockQty,
"category_id": utils.Str2Int64(storeSku.VendorCatID),
"predict_cat": 0, // 不使用推荐类目
"cat3_id": getEbaiCat(storeSku.VendorVendorCatID),
"weight": storeSku.Weight,
"photos": photos,
}
if storeSku.DescImg != "" {
params["rtf"] = storeSku.DescImg
}
if isCreate /*storeSku.SkuSyncStatus&(model.SyncFlagPriceMask| model.SyncFlagNewMask) != 0 */ {
params["sale_price"] = storeSku.VendorPrice
}
if storeSku.SkuSyncStatus&(model.SyncFlagSaleMask|model.SyncFlagNewMask) != 0 {
params["status"] = jxSkuStatus2Ebai(storeSku.MergedStatus)
}
// todo 饿百如果给的UPC是空要报错但如果我要删除UPC怎么弄
// if storeSku.Upc != "" {
// params["upc"] = storeSku.Upc
// }
return params
}
func ebaiSkuStatus2Jx(ebaiSkuStatus int) (jxSkuStatus int) {
if ebaiSkuStatus == ebaiapi.SkuStatusOnline {
jxSkuStatus = model.SkuStatusNormal
} else if ebaiSkuStatus == ebaiapi.SkuStatusOffline {
jxSkuStatus = model.SkuStatusDontSale
} else if ebaiSkuStatus == ebaiapi.SkuStatusOnline {
jxSkuStatus = model.SkuStatusDeleted
}
return jxSkuStatus
}
func jxSkuStatus2Ebai(status int) int {
if status <= 0 {
return ebaiapi.SkuStatusOffline
}
return ebaiapi.SkuStatusOnline
}
func getEbaiCat(catID int64) int64 {
if catID == 0 {
return defVendorCatID
}
return catID
}
// 饿百的排序是从大到小
func genSkuCatRank(storeSku *dao.StoreSkuSyncInfo) int {
return int(ebaiapi.MaxSkuCatRank - storeSku.GetSeq())
}
// 饿百的排序是从大到小
func jxCatSeq2Ebai(seq int) int {
return ebaiapi.MaxCatCatRank - seq
}
func formatCatName(name string) string {
return utils.LimitUTF8StringLen(name, ebaiapi.MaxCategoryNameLen)
}
func (p *PurchaseHandler) GetStoreSkusFullInfo(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (skuNameList []*partner.SkuNameInfo, err error) {
params := &ebaiapi.SkuListParams{
PageSize: ebaiapi.MaxSkuListPageSize,
}
if len(storeSkuList) == 1 {
if storeSkuList[0].SkuID > 0 {
params.CustomSkuID = utils.Int2Str(storeSkuList[0].SkuID)
}
if storeSkuList[0].VendorSkuID != "" {
params.SkuID = utils.Str2Int64WithDefault(storeSkuList[0].VendorSkuID, 0)
}
}
page1, err := api.EbaiAPI.SkuList(utils.Int2Str(storeID), params)
if err == nil {
skuNameList = append(skuNameList, vendorSkuList2Jx(page1.List)...)
if page1.Pages > 1 {
pages := make([]int, page1.Pages-1)
for i := 2; i <= page1.Pages; i++ {
pages[i-2] = i
}
task := tasksch.NewParallelTask("ebai GetStoreSkusFullInfo", nil, ctx,
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
callParams := &ebaiapi.SkuListParams{
PageSize: ebaiapi.MaxSkuListPageSize,
Page: batchItemList[0].(int),
}
pageSku, err2 := api.EbaiAPI.SkuList(utils.Int2Str(storeID), callParams)
if err2 == nil {
return pageSku.List, err2
}
return nil, err2
}, pages)
tasksch.HandleTask(task, parentTask, true).Run()
result, err2 := task.GetResult(0)
if err = err2; err == nil {
for _, v := range result {
skuNameList = append(skuNameList, vendorSku2Jx(v.(*ebaiapi.SkuInfo)))
}
}
}
}
return skuNameList, err
}
func vendorSku2Jx(vendorSku *ebaiapi.SkuInfo) (skuName *partner.SkuNameInfo) {
prefix, name, comment, specUnit, unit, specQuality := jxutils.SplitSkuName(vendorSku.Name)
weight := vendorSku.Weight
if weight <= 0 {
weight = jxutils.FormatSkuWeight(specQuality, specUnit)
}
skuID := int(utils.Str2Int64WithDefault(vendorSku.CustomSkuID, 0))
vendorSkuID := utils.Int64ToStr(vendorSku.SkuID)
skuName = &partner.SkuNameInfo{
NameID: skuID,
VendorNameID: vendorSkuID,
Prefix: prefix,
Name: name,
Unit: unit,
SkuList: []*partner.SkuInfo{
&partner.SkuInfo{
StoreSkuInfo: partner.StoreSkuInfo{
VendorSkuID: vendorSkuID,
SkuID: skuID,
Stock: vendorSku.LeftNum,
VendorPrice: vendorSku.SalePrice,
Status: ebaiSkuStatus2Jx(vendorSku.Status),
},
SkuName: vendorSku.Name,
Comment: comment,
SpecQuality: float64(specQuality),
SpecUnit: specUnit,
Weight: weight,
},
},
}
for _, v := range vendorSku.Photos {
skuName.PictureList = append(skuName.PictureList, v.URL)
}
// todo, 看起来饿百只返回了最低一级的商家分类信息
for _, v := range vendorSku.CustomCatList {
skuName.VendorCatIDList = append(skuName.VendorCatIDList, v.CustomCatID)
}
return skuName
}
func vendorSkuList2Jx(vendorSkuList []*ebaiapi.SkuInfo) (skuNameList []*partner.SkuNameInfo) {
for _, vendorSku := range vendorSkuList {
skuNameList = append(skuNameList, vendorSku2Jx(vendorSku))
}
return skuNameList
}
func (p *PurchaseHandler) GetSensitiveWordRegexp() *regexp.Regexp {
return sensitiveWordRegexp
}
//饿百api返回
func SelectStoreSkuListByOpResult(storeSkuList []*partner.StoreSkuInfo, opResult *ebaiapi.BatchOpResult, storeID, vendorID int, syncType string) (selectedStoreSkuList []*partner.StoreSkuInfoWithErr) {
opResultMap := make(map[int64]string)
if len(opResult.FailedList) > 0 {
for _, v := range opResult.FailedList {
opResultMap[v.SkuID] = v.ErrorMsg
}
for _, v := range storeSkuList {
if opResultMap[utils.Str2Int64(v.VendorSkuID)] != "" {
opFailed := &partner.StoreSkuInfoWithErr{
StoreSkuInfo: v,
ErrMsg: opResultMap[utils.Str2Int64(v.VendorSkuID)],
StoreID: storeID,
VendoreID: vendorID,
SyncType: syncType,
}
selectedStoreSkuList = append(selectedStoreSkuList, opFailed)
}
}
}
return selectedStoreSkuList
}
func (p *PurchaseHandler) CreateStoreSkusAct(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) {
for _, v := range storeSkuList {
if vendorActID, err2 := createOneShopAct(putils.GetFixDirectDownAct(storeID, v.SkuID), utils.Int2Str(storeID), putils.StoreSku2ActStoreSku([]*partner.StoreSkuInfo{v})); err2 != nil {
failedList = append(failedList, &partner.StoreSkuInfoWithErr{
StoreSkuInfo: v,
VendoreID: model.VendorIDJD,
StoreID: storeID,
ErrMsg: err.Error(),
})
} else {
v.VendorActID = vendorActID
}
}
return failedList, err
}
func (p *PurchaseHandler) CancelActs(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) {
for _, v := range storeSkuList {
if err2 := ActivityDisable(utils.Str2Int64(v.VendorActID), utils.Int2Str(storeID), 0, 0); err2 != nil {
failedList = append(failedList, &partner.StoreSkuInfoWithErr{
StoreSkuInfo: v,
VendoreID: model.VendorIDJD,
StoreID: storeID,
ErrMsg: err.Error(),
})
}
}
return failedList, err
}