Files
jx-callback/business/jxstore/cms/system_store_sku.go
邹宗楠 8fd42c70a0 1
2026-03-25 13:57:18 +08:00

1746 lines
61 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 cms
import (
"encoding/json"
"fmt"
"git.rosy.net.cn/baseapi/platformapi/ebaiapi"
"git.rosy.net.cn/baseapi/platformapi/jdapi"
product_addV2_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_addV2/request"
"git.rosy.net.cn/baseapi/platformapi/tiktok_shop/tiktok_api"
"git.rosy.net.cn/jx-callback/business/jxutils"
"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/purchase/mtwm"
"git.rosy.net.cn/jx-callback/business/partner/purchase/tiktok_store"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
beego "github.com/astaxie/beego/server/web"
"strings"
"time"
"git.rosy.net.cn/baseapi/platformapi/mtwmapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
)
const (
SyncTypeSku = 1
SyncTypeAct = 2
SyncTypeAll = 3
)
// CopyOnStoreSkuToOther 将一个美团门店分类和商品复制到另一个门店
func CopyOnStoreSkuToOther(ctx *jxcontext.Context, fromStoreId, toStoreId string, vendorId int, isAsync bool, offSet int, syncType int) ([]string, error) {
var (
db = dao.GetDB()
copySkuErr = make([]string, 0, 0)
err error
fromVendorID = 0
toVendorID = 0
)
if vendorId > 10 {
fromVendorID = utils.Str2Int(string(utils.Int2Str(vendorId)[0]))
toVendorID = utils.Str2Int(string(utils.Int2Str(vendorId)[1:]))
} else {
fromVendorID = vendorId
toVendorID = vendorId
}
// 门店api加载
toStore, err := dao.GetStoreDetailByVendorStoreID(db, toStoreId, toVendorID, "")
if err != nil {
return nil, err
}
fromStore, err := dao.GetStoreDetailByVendorStoreID(db, fromStoreId, fromVendorID, "")
if err != nil {
return nil, err
}
if syncType == SyncTypeSku || syncType == SyncTypeAll {
switch vendorId {
case model.VendorIDMTWM:
_, copySkuErr, err = CopyMtToMT(ctx, fromStore, toStore, isAsync, offSet)
case model.VendorIDEBAI:
_, copySkuErr, err = CopyEBaiToEBai(ctx, fromStore, toStore, isAsync, offSet)
case 114: // 美团到抖音
_, copySkuErr, err = CopyMtToTiktok(ctx, fromStore, toStore, isAsync, offSet)
case 13: // 美团到饿了么
_, copySkuErr, err = CopyMtToEBai(ctx, fromStore, toStore, isAsync, offSet)
case 31: // 饿了么到美团
_, copySkuErr, err = CopyEBaiToMt(ctx, fromStore, toStore, isAsync, offSet)
case 30: // 饿百到京东
_, copySkuErr, err = CopyEBaiToJd(ctx, fromStore, toStore, isAsync, offSet)
default:
return nil, fmt.Errorf("暂时还不支持")
}
}
if syncType == SyncTypeAct || syncType == SyncTypeAll {
if vendorId == model.VendorIDMTWM {
}
}
return copySkuErr, err
}
//#region 饿百同步商品
// CopyEBaiToEBai 饿了么商品复制到饿了么
func CopyEBaiToEBai(ctx *jxcontext.Context, fromStore, toStore *dao.StoreDetail, isAsync bool, offset int) (hint string, errList []string, err error) {
VendorCategoryIDMap := map[int64]int64{}
var errListData = make([]string, 0, 0)
api := api.EbaiAPI
taskName := fmt.Sprintf("将饿了么平台门店[%s],分类和商品复制到[%s]", fromStore.VendorStoreID, toStore.VendorStoreID)
config := tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(false)
work := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
step := batchItemList[0].(int)
switch step {
case 1:
// 1.加载门店商品,删除商品.当分类下没有商品时.删除分类
//errs := LoadingStoreSkuList(ctx, toApi, toStore.VendorStoreID)
// errs := LoadingStoreSkuList(ctx, toApi, toStore.VendorStoreID)
//if errs != nil && len(errs) > 0 {
// return nil, errs[0]
//}
case 2:
//同步分类
fromCategoryList, err := api.ShopCategoryGet(utils.Int2Str(fromStore.ID))
if err != nil {
return nil, err
}
for _, v := range fromCategoryList {
parentID, categoryErr := api.ShopCategoryCreate(utils.Int2Str(toStore.ID), 0, v.Name, v.Rank)
if categoryErr != nil {
globals.SugarLogger.Debugf("err := RetailCatUpdate : %v", categoryErr)
continue
}
VendorCategoryIDMap[v.CategoryID] = parentID
if v.Children != nil && len(v.Children) != 0 {
for _, c := range v.Children {
childrenCateId, err := api.ShopCategoryCreate(utils.Int2Str(toStore.ID), parentID, c.Name, c.Rank)
if err != nil {
globals.SugarLogger.Debugf("err := RetailCatUpdate Children : %v", err)
continue
}
VendorCategoryIDMap[c.CategoryID] = childrenCateId
}
}
}
case 3:
i := 1
for {
// 同步商品
fromFoodList, err1 := api.SkuList(utils.Int2Str(fromStore.ID), &ebaiapi.SkuListParams{
Page: i,
})
if len(fromFoodList.List) == 0 || fromFoodList == nil {
return nil, fmt.Errorf("fromFoodList 为空 %s ,i:= %d", utils.Format4Output(err1, false), i)
}
errList := BatchInitSkuEBai2EBai(ctx, fromFoodList.List, api, utils.Int2Str(toStore.ID), VendorCategoryIDMap)
if errList != nil {
for _, verr := range errList {
errListData = append(errListData, verr.Error())
}
globals.SugarLogger.Debugf("BatchInitData : %s", utils.Format4Output(errList, false))
}
i = i + 1
}
}
return
}
task := tasksch.NewParallelTask(taskName, config, ctx, work, []int{1, 2, 3})
tasksch.HandleTask(task, nil, true).Run()
if !isAsync {
_, err = task.GetResult(0)
hint = "1"
} else {
hint = task.ID
}
return hint, errList, err
}
// BatchInitSkuEBai2EBai 批量创建商品
func BatchInitSkuEBai2EBai(ctx *jxcontext.Context, fromSku []*ebaiapi.SkuInfo, toApi *ebaiapi.API, storeID string, VendorCategoryIDMap map[int64]int64) []error {
var errList = make([]error, 0, 0)
for _, storeSku := range fromSku {
params := map[string]interface{}{
"left_num": model.MaxStoreSkuStockQty,
"weight": storeSku.Weight,
"photos": storeSku.Photos,
"preparation_time": storeSku.PreparationTime,
}
params["upc"] = storeSku.Upc
params["name"] = storeSku.Name
params["cat3_id"] = storeSku.CateId
params["category_id"] = VendorCategoryIDMap[utils.Str2Int64(storeSku.CustomCatList[0].CustomCatID)]
params["rtf"] = storeSku.Rtf
params["left_num"] = storeSku.LeftNum
params["process_type"] = storeSku.ProcessType
params["process_detail"] = storeSku.ProcessDetail
params["sale_price"] = storeSku.SalePrice
params["status"] = storeSku.Status
params["minimum"] = storeSku.Minimum
params["seven_days_no_reason"] = storeSku.SevenDaysNoReason
if len(storeSku.SkuProperty) != model.NO {
params["sku_property"] = storeSku.SkuProperty
}
customSkuID := storeSku.CustomSkuID
if customSkuID == "" {
customSkuID = utils.Int64ToStr(storeSku.SkuId)
}
_, err := toApi.SkuCreate(ctx.GetTrackInfo(), storeID, utils.Str2Int64(storeSku.CustomSkuID), params)
if err != nil {
errList = append(errList, err)
}
}
return errList
}
//#endregion
//#region 美团同步商品
// CopyMtToMT 美团商品复制到美团
func CopyMtToMT(ctx *jxcontext.Context, fromStore, toStore *dao.StoreDetail, isAsync bool, offSet int) (hint string, data []string, err error) {
var fromApi *mtwmapi.API
var toApi *mtwmapi.API
var errList = make([]*mtwmapi.AppFoodResult, 0, 0)
var errData = make([]string, 0, 0)
if fromStore.VendorOrgCode == globals.Mtwm2Code {
fromApi = mtwmapi.New(beego.AppConfig.DefaultString("mtwmAppID2", ""), beego.AppConfig.DefaultString("mtwmSecret2", ""), beego.AppConfig.DefaultString("mtwmCallbackURL2", ""), "")
fromApi.SetToken(fromStore.MtwmToken)
} else {
fromApi = partner.CurAPIManager.GetAPI(model.VendorIDMTWM, fromStore.VendorOrgCode).(*mtwmapi.API)
}
if toStore.VendorOrgCode == globals.Mtwm2Code {
toApi = mtwmapi.New(beego.AppConfig.DefaultString("mtwmAppID2", ""), beego.AppConfig.DefaultString("mtwmSecret2", ""), beego.AppConfig.DefaultString("mtwmCallbackURL2", ""), "")
toApi.SetToken(toStore.MtwmToken)
} else {
toApi = partner.CurAPIManager.GetAPI(model.VendorIDMTWM, toStore.VendorOrgCode).(*mtwmapi.API)
}
taskName := fmt.Sprintf("将美团平台门店[%s],分类和商品复制到[%s]", fromStore.VendorStoreID, toStore.VendorStoreID)
config := tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(false)
work := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
step := batchItemList[0].(int)
switch step {
case 1:
// 同步分类
fromCategoryList, err := fromApi.RetailCatList(fromStore.VendorStoreID)
if len(fromCategoryList) == model.NO {
return nil, err
}
//toCategoryList, err := toApi.RetailCatList(toStore.VendorStoreID)
//if err != nil {
// return nil, err
//}
for _, v := range fromCategoryList {
categoryErr := toApi.RetailCatUpdate(toStore.VendorStoreID, v.Name, &mtwmapi.Param4UpdateCat{
CategoryCode: v.Code,
Sequence: v.Sequence,
})
if categoryErr != nil {
globals.SugarLogger.Debugf("err := RetailCatUpdate : %v", categoryErr)
}
if v.Children != nil && len(v.Children) != 0 {
for _, c := range v.Children {
if err3 := toApi.RetailCatUpdate(toStore.VendorStoreID, v.Name, &mtwmapi.Param4UpdateCat{
CategoryNameOrigin: v.Name,
SecondaryCategoryCode: c.Code,
SecondaryCategoryName: c.Name,
Sequence: c.Sequence,
}); err3 != nil {
globals.SugarLogger.Debugf("err := RetailCatUpdate Children : %v", err3)
}
}
}
}
case 2:
i := offSet
for {
// 同步商品
fromFoodList, err1 := fromApi.RetailListAll(fromStore.VendorStoreID, i)
if len(fromFoodList) == 0 || fromFoodList == nil {
return nil, fmt.Errorf("fromFoodList 为空 %s ,i:= %d", utils.Format4Output(err1, false), i)
}
errDataList, err := BatchInitSkuMT2MT(ctx, fromFoodList, toApi, toStore.VendorStoreID, i)
if err != nil {
globals.SugarLogger.Debugf("BatchInitData : %v", err)
}
if len(errDataList) > model.NO {
globals.SugarLogger.Debugf("errListData %d:= %s", i, utils.Format4Output(errDataList, false))
errList = append(errList, errDataList...)
}
globals.SugarLogger.Debugf("==========页数[%d],数据长度[%d]", i, len(fromFoodList))
if len(fromFoodList) < 100 {
break
}
i++
}
}
return
}
task := tasksch.NewParallelTask(taskName, config, ctx, work, []int{1, 2})
tasksch.HandleTask(task, nil, true).Run()
if !isAsync {
_, err = task.GetResult(0)
hint = "1"
} else {
hint = task.ID
}
globals.SugarLogger.Debugf("======errrList := %s", utils.Format4Output(errList, false))
for _, v := range errList {
errData = append(errData, fmt.Sprintf("food_id:%s,错误:%s", v.AppFoodCode, v.ErrorMsg))
}
return hint, errData, err
}
// BatchInitSkuMT2MT 批量创建商品
func BatchInitSkuMT2MT(ctx *jxcontext.Context, fromSku []*mtwmapi.AppFood, toApi *mtwmapi.API, vendorStoreID string, offset int) ([]*mtwmapi.AppFoodResult, error) {
errList := make([]*mtwmapi.AppFoodResult, 0, 0)
foodDataList := make([]map[string]interface{}, len(fromSku))
nameList := make(map[string]string, len(fromSku))
//apiObj := apimanager.CurAPIManager.GetAPI(model.VendorIDJD, "320406").(*jdapi.API)
for k, storeSku := range fromSku {
foodData := make(map[string]interface{})
if storeSku.AppFoodCode != "" {
foodData[mtwmapi.KeyAppFoodCode] = storeSku.AppFoodCode
} else {
foodData[mtwmapi.KeyAppFoodCode] = utils.Int64ToStr(time.Now().UnixNano() + int64(k*3))
}
skus := make([]interface{}, 0)
upc := ""
for _, v := range storeSku.SkuList {
if v.Stock == "" {
v.Stock = "99999"
}
if v.SkuId == "" {
v.SkuId = storeSku.AppFoodCode
}
if v.SkuId == "" {
v.SkuId = utils.Int2Str(storeSku.Ctime + int(k*3))
}
// salesCycle, _ := json.Marshal(v.AvailableTimes)
mapSkuList := utils.Struct2MapByJson(v)
if v.BoxNum == "" {
delete(mapSkuList, "box_num")
}
if v.BoxPrice == "" {
delete(mapSkuList, "box_price")
}
if v.LadderBoxNum == "" {
delete(mapSkuList, "ladder_box_num")
}
if v.LadderBoxPrice == "" {
delete(mapSkuList, "ladder_box_price")
}
delete(mapSkuList, "min_order_count")
if v.AvailableTimes != nil {
delete(mapSkuList, "available_times")
}
if v.Upc != "" {
upc = v.Upc
} /* else if toApi.QuerySkuIsNeedUpc(storeSku.TagID) {
upcList, _ := apiObj.GetJdUpcCodeByName(storeSku.Name, "", 1, 20)
if len(upcList) != 0 {
upc = upcList[0].UpcCode
}
}*/
//mapSkuList["available_times"] = string(salesCycle)
//mapSkuList["available_times"] = v.AvailableTimes
//if v.MinOrderCount != "" {
// mapSkuList["min_order_count"] = utils.Str2Int(v.MinOrderCount)
//}
if upc != "" {
mapSkuList["upc"] = upc
}
skus = append(skus, mapSkuList)
}
foodData["skus"] = skus
foodData["name"] = utils.LimitUTF8StringLen(storeSku.Name, mtwmapi.MaxSkuNameCharCount)
foodData["description"] = storeSku.Description
foodData["price"] = storeSku.Price
foodData["min_order_count"] = storeSku.MinOrderCount
foodData["unit"] = storeSku.Unit
if storeSku.SecondaryCategoryCode != "" {
foodData["category_code"] = storeSku.SecondaryCategoryCode
} else {
foodData["category_name"] = storeSku.SecondaryCategoryName
}
if storeSku.SecondaryCategoryName == "" && storeSku.SecondaryCategoryCode == "" {
if storeSku.SecondaryCategoryCode != "" {
foodData["category_code"] = storeSku.CategoryCode
} else {
foodData["category_name"] = storeSku.CategoryName
}
}
//todo 增加商品必填属性
//foodData["common_attr_value"] = storeSku.CommonAttrValue
commonAttrValue, err := createCommonAttrValue(toApi, utils.Interface2Int64WithDefault(foodData["category_code"], 0), utils.Interface2String(foodData["name"]))
if err == nil {
foodData["common_attr_value"] = commonAttrValue
} else if storeSku.CommonAttrValue != "" && storeSku.CommonAttrValue != "[]" {
foodData["common_attr_value"] = mtwmapi.CommonAttrValueUpdate(storeSku.CommonAttrValue)
}
foodData["is_sold_out"] = storeSku.IsSoldOut
foodData["picture"] = storeSku.Picture
foodData["picture_contents"] = storeSku.PictureContents
foodData["sequence"] = storeSku.Sequence
foodData["tag_id"] = storeSku.TagID
foodData["upc"] = storeSku.UpcCode
if foodData["upc"].(string) == "" {
foodData["upc"] = upc
}
if foodData["upc"].(string) == "" {
foodData["upc"] = "no_upc"
}
foodDataList[k] = foodData
nameList[foodData[mtwmapi.KeyAppFoodCode].(string)] = foodData["name"].(string)
}
count := len(foodDataList) / 10
if len(foodDataList)%10 != 0 {
count += 1
}
for i := 0; i < count; i++ {
if i == count-1 {
failedFoodList, _ := toApi.RetailBatchInitData(ctx.GetTrackInfo(), vendorStoreID, foodDataList[i*10:])
if len(failedFoodList) != 0 {
errList = append(errList, failedFoodList...)
for _, ffl := range failedFoodList {
if nameList[ffl.AppFoodCode] != "" {
globals.SugarLogger.Debugf("name := %s,err := %s", nameList[ffl.AppFoodCode], ffl.ErrorMsg)
}
}
}
} else {
failedFoodList, _ := toApi.RetailBatchInitData(ctx.GetTrackInfo(), vendorStoreID, foodDataList[i*10:(i+1)*10])
if len(failedFoodList) != 0 {
errList = append(errList, failedFoodList...)
for _, ffl := range failedFoodList {
if nameList[ffl.AppFoodCode] != "" {
globals.SugarLogger.Debugf("name := %s,err := %s", nameList[ffl.AppFoodCode], ffl.ErrorMsg)
}
}
}
}
}
return errList, nil
}
func createCommonAttrValue(apiObj *mtwmapi.API, tempCatID int64, name string) (string, error) {
var (
attrValue mtwm.CommonAttrValue
attrValues []mtwm.CommonAttrValue
)
attrList, err := apiObj.CategoryAttrList(tempCatID)
if err == nil && len(attrList) > 0 {
for _, v := range attrList {
if v.Need == mtwmapi.NeedYes {
attrValue = mtwm.CommonAttrValue{
AttrID: utils.Str2Int(v.AttrID),
AttrName: v.AttrName,
}
if v.AttrID == mtwmapi.SpecialAttrBrand || v.AttrID == mtwmapi.SpecialAttrProducer { //单独获取特殊属性
data, err1 := apiObj.CategoryAttrValueList(utils.Str2Int64(v.AttrID), name)
if len(data) > 0 {
attrValue.ValueList = []mtwm.ValueList{{ //默认取推荐第一个
ValueID: utils.Str2Int(data[0].ValueID),
Value: data[0].Value,
}}
}
if err1 != nil || len(data) == 0 {
attrValue.ValueList = []mtwm.ValueList{{ //兜底处理
Value: "其他",
}}
}
} else {
if len(v.ValueList) > 0 {
attrValue.ValueList = []mtwm.ValueList{{
ValueID: utils.Str2Int(v.ValueList[0].ValueID),
Value: v.ValueList[0].Value,
}}
} else { //兜底处理
attrValue.ValueList = []mtwm.ValueList{{
Value: "其他",
}}
}
}
attrValues = append(attrValues, attrValue)
}
}
temp, err := json.Marshal(attrValues)
return string(temp), err
}
return "", fmt.Errorf("暂未获取到")
}
//#endregion
//#region 美团同步到抖音商品
// CopyMtToTiktok 复制美团商品到抖音
func CopyMtToTiktok(ctx *jxcontext.Context, fromStore, toStore *dao.StoreDetail, isAsync bool, offSet int) (hint string, data []string, err error) {
var fromApi *mtwmapi.API //= mtwm.GetAPI(fromStore.VendorOrgCode, fromStore.ID, fromStore.VendorStoreID)
var toApi = partner.CurAPIManager.GetAPI(model.VendorIDDD, toStore.VendorOrgCode).(*tiktok_api.API)
var errList = make([]*mtwmapi.AppFoodResult, 0, 0)
var errData = make([]string, 0, 0)
if fromStore.VendorOrgCode == globals.Mtwm2Code {
fromApi = mtwmapi.New(beego.AppConfig.DefaultString("mtwmAppID2", ""), beego.AppConfig.DefaultString("mtwmSecret2", ""), beego.AppConfig.DefaultString("mtwmCallbackURL2", ""), "")
fromApi.SetToken(fromStore.MtwmToken)
} else {
fromApi = partner.CurAPIManager.GetAPI(model.VendorIDMTWM, fromStore.VendorOrgCode).(*mtwmapi.API)
}
taskName := fmt.Sprintf("将美团平台门店[%s],分类和商品复制到抖音[%s]", fromStore.VendorStoreID, toStore.VendorStoreID)
config := tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(false)
work := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
step := batchItemList[0].(int)
switch step {
case 1:
case 2:
i := offSet
for {
// 同步商品
fromFoodList, err1 := fromApi.RetailListAll(fromStore.VendorStoreID, i)
if len(fromFoodList) == 0 || fromFoodList == nil {
return nil, fmt.Errorf("fromFoodList 为空 %s ,i:= %d", utils.Format4Output(err1, false), i)
}
errDataList, err := BatchInitSkuMT2TT(ctx, fromFoodList, fromStore.VendorStoreID, toApi, toStore, i)
if err != nil {
globals.SugarLogger.Debugf("BatchInitData : %v", err)
}
if len(errDataList) > model.NO {
globals.SugarLogger.Debugf("errListData %d:= %s", i, utils.Format4Output(errDataList, false))
errList = append(errList, errDataList...)
}
globals.SugarLogger.Debugf("==========页数[%d],数据长度[%d]", i, len(fromFoodList))
if len(fromFoodList) < 100 {
break
}
i++
}
}
return
}
task := tasksch.NewParallelTask(taskName, config, ctx, work, []int{1, 2})
tasksch.HandleTask(task, nil, true).Run()
if !isAsync {
_, err = task.GetResult(0)
hint = "1"
} else {
hint = task.ID
}
globals.SugarLogger.Debugf("======errrList := %s", utils.Format4Output(errList, false))
for _, v := range errList {
errData = append(errData, fmt.Sprintf("food_id:%s,错误:%s", v.AppFoodCode, v.ErrorMsg))
}
return hint, errData, err
}
var StoreTemp = make(map[string]string, 0)
// BatchInitSkuMT2TT 批量创建商品美团->抖音
func BatchInitSkuMT2TT(ctx *jxcontext.Context, fromSku []*mtwmapi.AppFood, fromStoreId string, toApi *tiktok_api.API, toStoreDetail *dao.StoreDetail, offset int) ([]*mtwmapi.AppFoodResult, error) {
errList := make([]*mtwmapi.AppFoodResult, 0, 0)
db := dao.GetDB()
copyMap, _ := dao.GetCopyInfo(fromStoreId, model.VendorIDMTWM, toStoreDetail.VendorStoreID, model.VendorIDDD)
for k, storeSku := range fromSku {
if storeSku.AppFoodCode == "" {
storeSku.AppFoodCode = fmt.Sprintf("%d_%d", storeSku.Ctime, k)
}
// 没查询到主品创建记录,创建主品
if copyMap[storeSku.AppFoodCode] == nil || copyMap[storeSku.AppFoodCode].MainSkuId == "" {
param := &product_addV2_request.ProductAddV2Param{
Name: utils.LimitUTF8StringLen(storeSku.Name, 90),
PayType: utils.Int64ToPointer(tiktok_api.TiktokPayType1),
ReduceType: tiktok_api.SkuReduceTypePayMakeOrder,
DeliveryDelayDay: utils.Int64ToPointer(tiktok_api.DeliveryDelayDayToDay),
PresellType: utils.Int64ToPointer(tiktok_api.SendGoodsTypeNow),
Supply7dayReturn: utils.Int64ToPointer(0), // 是否支持7天无理由0不支持1支持2支持拆封后不支持
Mobile: toStoreDetail.Tel1,
Commit: true,
//Specs: "重量|" + utils.Float64ToStr(float64(storeSku.SpecQuality)) + storeSku.SpecUnit,
NeedRechargeMode: nil,
SellChannel: []int64{0},
StartSaleType: utils.Int64ToPointer(0),
PickupMethod: utils.String2Pointer("0"),
OuterProductId: utils.String2Pointer(storeSku.AppFoodCode), // 本地skuId为外部商品id
}
specs := "重量|"
specsList := make([]string, 0, 0)
upc := ""
for _, sl := range storeSku.SkuList {
specsList = append(specsList, strings.Split(sl.Spec, "*")[0])
param.Weight = utils.Float64ToPointer(utils.Str2Float64(sl.Weight))
upc = sl.Upc
}
param.Specs = utils.String2Pointer(specs + strings.Join(specsList, ","))
// 获取上传图,商品轮播图
imgs := make([]tiktok_api.Imgs, 0, 0)
for spk, spl := range storeSku.PictureList {
switch spk {
case 0:
imgs = append(imgs, tiktok_api.Imgs{
Name: "white_" + utils.Int2Str(toStoreDetail.ID) + "_" + spl[21:54],
Url: spl,
})
case len(storeSku.PictureList) - 1:
imgs = append(imgs, tiktok_api.Imgs{
Name: "detail_" + utils.Int2Str(toStoreDetail.ID) + "_" + spl[21:54],
Url: spl,
})
default:
imgs = append(imgs, tiktok_api.Imgs{
Name: utils.Int2Str(toStoreDetail.ID) + "_" + spl[21:54],
Url: spl,
})
}
}
tiktokImgList, err := toApi.BatchUploadImages(imgs)
if err != nil {
errList = append(errList, &mtwmapi.AppFoodResult{
AppFoodCode: storeSku.AppFoodCode,
ErrorMsg: fmt.Sprintf("%s:%s,图片上传错误", storeSku.Name, err.Error()),
})
continue
}
picList := make([]string, 0, 0)
for til, v := range tiktokImgList {
if strings.Contains(til, "detail_") {
param.Description = v.ByteUrl
continue
}
if strings.Contains(til, "white_") {
param.WhiteBackGroundPicUrl = utils.String2Pointer(v.ByteUrl)
}
picList = append(picList, v.ByteUrl)
}
param.Pic = strings.Join(picList, "|")
// 自动推导分类id
if len(param.Pic) != 0 {
var vendorCategoryId int64 = 0
// 根据图片推导分类
picList = append(picList, *param.WhiteBackGroundPicUrl, param.Description)
vendorCategoryId, _ = toApi.GetRecommendCategory(picList)
// 根据名字推导分类
if vendorCategoryId == 0 {
vendorCategoryId, _ = toApi.GetRecommendCategoryByName(param.Name)
}
if vendorCategoryId == 0 || err != nil {
errList = append(errList, &mtwmapi.AppFoodResult{
AppFoodCode: storeSku.AppFoodCode,
ErrorMsg: fmt.Sprintf("%s:%s,自动推导分类错误", storeSku.Name, err.Error()),
})
continue
}
param.CategoryLeafId = vendorCategoryId
}
// 是否支持七天无理由
isAfterSale, rule := toApi.GetProductUpdateRule(param.CategoryLeafId)
if isAfterSale {
param.AfterSaleService = &map[string]string{"supply_day_return_selector": fmt.Sprintf("%s", rule)}
}
// weight_unit 目前抖音只支持g和kg两种
param.WeightUnit = utils.Int64ToPointer(tiktok_api.WeightUint_G)
// spec_prices
//param.SpecPrices = GetSpecPrices(param.Specs, vendorStoreID, 0, storeSku)
skuSize := make([]*tiktok_api.SpecDetailList, 0, 0)
name1 := strings.Split(strings.Split(*param.Specs, "|")[1], ",")
for _, sl := range storeSku.SkuList {
for i := 0; i < len(name1); i++ {
if name1[i] == strings.Split(sl.Spec, "*")[0] {
sku := &tiktok_api.SpecDetailList{
SpecDetailName1: name1[i],
Price: utils.Float64TwoInt(utils.Str2Float64(sl.Price)),
Code: storeSku.AppFoodCode + fmt.Sprintf("_%d", i),
StepStockNum: 0,
SupplierID: "",
OuterSkuID: storeSku.AppFoodCode + fmt.Sprintf("_%d", i),
}
sku.DeliveryInfos = []*tiktok_api.DeliveryInfos{
{InfoType: "weight", InfoUnit: name1[i][len(name1[i])-1:], InfoValue: name1[i]},
}
sku.StockNum = utils.Str2Int(sl.Stock)
sku.SkuType = 1
sku.StockNumMap = map[string]int64{toStoreDetail.VendorStoreID: int64(sku.StockNum)}
skuSize = append(skuSize, sku)
}
}
}
data, _ := json.Marshal(skuSize)
param.SpecPrices = utils.String2Pointer(string(data))
// 获取商品的属性
//param.ProductFormatNew, param.StandardBrandId, err = MakeProductFormatNew(api, int64(storeSku.NameID), param.CategoryLeafId, storeSku.Upc, storeSku.UpcBrandName, storeSku.UpcTiktokBrandId)
if upc == "" {
param.StandardBrandId = utils.Int64ToPointer(596120136)
} else {
brandName, err := tiktok_store.GetBrandByBrandName(upc)
if err != nil {
param.StandardBrandId = utils.Int64ToPointer(596120136)
} else {
if strings.Contains(brandName, "/") || strings.Contains(brandName, "(") || strings.Contains(brandName, "") {
brandName = strings.Split(brandName, "/")[0]
}
if strings.Contains(brandName, "(") {
brandName = strings.Split(brandName, "(")[0]
}
if strings.Contains(brandName, "") {
brandName = strings.Split(brandName, "")[0]
}
standardBrandId, err := toApi.GetSkuBrand(param.CategoryLeafId, brandName)
if err != nil {
param.StandardBrandId = utils.Int64ToPointer(596120136)
} else {
param.StandardBrandId = &standardBrandId
}
}
}
if param.StandardBrandId == utils.Int64ToPointer(0) {
param.StandardBrandId = utils.Int64ToPointer(596120136)
}
categoryList, err := toApi.GetCatePropertyV2(param.CategoryLeafId)
if err != nil {
errList = append(errList, &mtwmapi.AppFoodResult{
AppFoodCode: storeSku.AppFoodCode,
ErrorMsg: fmt.Sprintf("%s:%s,根据商品分类推导属性错误", storeSku.Name, err.Error()),
})
continue
}
categoryMap := make(map[string][]map[string]interface{})
for _, v := range categoryList.Data.Data {
if v.Required != model.YES {
continue
}
options := make([]map[string]interface{}, 0)
if v.PropertyName == "品牌" {
options = append(options, map[string]interface{}{"name": v.PropertyName, "value": param.StandardBrandId, "diy_type": v.DiyType})
categoryMap[utils.Int64ToStr(v.PropertyId)] = options
} else if v.PropertyName == "产地" {
options = append(options, map[string]interface{}{"name": v.PropertyName, "value": 13850, "diy_type": v.DiyType})
categoryMap[utils.Int64ToStr(v.PropertyId)] = options
} else if len(options) == 0 {
options = append(options, map[string]interface{}{"name": v.PropertyName, "value": 0, "diy_type": v.DiyType})
categoryMap[utils.Int64ToStr(v.PropertyId)] = options
} else {
options = append(options, map[string]interface{}{"name": v.PropertyName, "value": v.Options[0].Value, "diy_type": v.DiyType})
categoryMap[utils.Int64ToStr(v.PropertyId)] = options
}
}
param.ProductFormatNew = utils.String2Pointer(utils.Format4Output(categoryMap, false))
//param.ProductFormatNew, err = MakeProductFormatNew(api, int64(storeSku.NameID), param.CategoryLeafId)
if StoreTemp[toStoreDetail.VendorStoreID] != "" {
idList := strings.Split(StoreTemp[toStoreDetail.VendorStoreID], "_")
param.FreightId, param.SaleLimitId = utils.Str2Int64(idList[0]), utils.Int64ToPointer(utils.Str2Int64(idList[1]))
} else {
// 运费模板
param.FreightId, err = tiktok_store.GetDeliveryTemp(toApi, toStoreDetail.VendorStoreID, toStoreDetail)
if err != nil {
errList = append(errList, &mtwmapi.AppFoodResult{
AppFoodCode: storeSku.AppFoodCode,
ErrorMsg: fmt.Sprintf("%s:%s,运费模版获取错误", storeSku.Name, err.Error()),
})
return errList, nil
}
// 获取门店限售模板
saleLimitId, err := tiktok_store.CreateSaleTemp(utils.Str2Int64(toStoreDetail.VendorStoreID), toApi)
if err != nil {
errList = append(errList, &mtwmapi.AppFoodResult{
AppFoodCode: storeSku.AppFoodCode,
ErrorMsg: fmt.Sprintf("%s:%s,限售模版获取错误", storeSku.Name, err.Error()),
})
return errList, nil
}
param.SaleLimitId = &saleLimitId
StoreTemp[toStoreDetail.VendorStoreID] = fmt.Sprintf("%d_%d", param.FreightId, param.SaleLimitId)
}
tiktokResult, errCreate := toApi.CreateStoreCommodity(param) // 创建主商品,同步主商品
copyData := &model.CopyVendorSku{
FromSkuID: storeSku.AppPoiCode,
FromSkuName: storeSku.Name,
FromStoreId: fromStoreId,
FromVendorId: model.VendorIDMTWM,
ToStoreId: toStoreDetail.VendorStoreID,
ToVendorId: model.VendorIDDD,
MainSkuId: "",
ChildrenSkuId: "",
ErrMsg: "",
}
if errCreate != nil {
copyData.ErrMsg = errCreate.Error()
}
if tiktokResult.ProductId != 0 {
copyData.MainSkuId = utils.Int64ToStr(tiktokResult.ProductId)
}
dao.CreateEntity(db, copyData)
} else {
// 主商品存在,直接同步子商品
childrenProductId, _, err := toApi.CreateSubProduct(utils.Str2Int64(copyMap[storeSku.AppPoiCode].MainSkuId), utils.Str2Int64(toStoreDetail.VendorStoreID))
// 2010004:主商品非在线审核通过状态,不允许绑定子商品
if err != nil && strings.Contains(err.Error(), "2010004") {
// 线上本地都存在,但是线上审核不成功,就去更新主商品
copyMap[storeSku.AppPoiCode].ErrMsg = err.Error()
dao.UpdateEntity(db, copyMap[storeSku.AppPoiCode], "ErrMsg")
continue
}
if err != nil && strings.Contains(err.Error(), "2010001") { // 2010001:重复创建渠道商品
storeSkuDetail, err := toApi.GetSkuDetailLocalID(toStoreDetail.VendorStoreID, storeSku.AppFoodCode)
if err != nil {
copyMap[storeSku.AppPoiCode].ErrMsg = err.Error()
dao.UpdateEntity(db, copyMap[storeSku.AppPoiCode], "ErrMsg")
continue
}
childrenProductId = storeSkuDetail.ProductId
copyMap[storeSku.AppPoiCode].ChildrenSkuId = utils.Int64ToStr(childrenProductId)
dao.UpdateEntity(db, copyMap[storeSku.AppPoiCode], "ChildrenSkuId")
}
if childrenProductId != model.NO {
// 同步价格,库存,上架
//childrenDetail, err := toApi.GetSkuDetail(utils.Int64ToStr(childrenProductId), "")
//if err != nil {
// copyMap[storeSku.AppPoiCode].ErrMsg = err.Error()
// dao.UpdateEntity(db, copyMap[storeSku.AppPoiCode], "ErrMsg")
// continue
//}
//for _, v := range childrenDetail.SpecPrices {
// childrenSkuId := v.SkuId
// storeSku.VendorSonSkuID = utils.Int64ToStr(childrenSkuId)
// return childrenSkuId, nil
//}
//failedList2 := upDateChildrenPriceStockLaunch(api, storeSku, childrenProductId, vendorStoreID, syncType)
if err := toApi.LaunchProduct(childrenProductId); err != nil {
copyMap[storeSku.AppPoiCode].ErrMsg = "上架失败" + err.Error()
dao.UpdateEntity(db, copyMap[storeSku.AppPoiCode], "ChildrenSkuId")
}
}
}
}
return errList, nil
}
//#endregion
//#region 美团同步到饿百
// CopyMtToEBai 复制美团商品到饿百
func CopyMtToEBai(ctx *jxcontext.Context, fromStore, toStore *dao.StoreDetail, isAsync bool, offSet int) (hint string, data []string, err error) {
VendorCategoryIDMap := map[string]int64{}
var fromApi *mtwmapi.API //= mtwm.GetAPI(fromStore.VendorOrgCode, fromStore.ID, fromStore.VendorStoreID)
var ebaiApi = api.EbaiAPI
var errListData = make([]string, 0, 0)
if fromStore.VendorOrgCode == globals.Mtwm2Code {
fromApi = mtwmapi.New(beego.AppConfig.DefaultString("mtwmAppID2", ""), beego.AppConfig.DefaultString("mtwmSecret2", ""), beego.AppConfig.DefaultString("mtwmCallbackURL2", ""), "")
fromApi.SetToken(fromStore.MtwmToken)
globals.SugarLogger.Debugf("------fromApi- := %s", "1111")
} else {
fromApi = partner.CurAPIManager.GetAPI(model.VendorIDMTWM, fromStore.VendorOrgCode).(*mtwmapi.API)
}
taskName := fmt.Sprintf("将美团平台门店[%s],分类和商品复制到饿百[%s]", fromStore.VendorStoreID, toStore.VendorStoreID)
config := tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(false)
work := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
step := batchItemList[0].(int)
switch step {
case 1:
// 1.加载门店商品,删除商品.当分类下没有商品时.删除分类
//errs := LoadingStoreSkuList(ctx, toApi, toStore.VendorStoreID)
// errs := LoadingStoreSkuList(ctx, toApi, toStore.VendorStoreID)
//if errs != nil && len(errs) > 0 {
// return nil, errs[0]
//}
case 2:
//同步分类
fromCategoryList, err := fromApi.RetailCatList(fromStore.VendorStoreID)
if len(fromCategoryList) == model.NO {
return nil, err
}
for k, v := range fromCategoryList {
parentID, categoryErr := ebaiApi.ShopCategoryCreate(utils.Int2Str(toStore.ID), 0, v.Name, len(fromCategoryList)-k)
if categoryErr != nil {
globals.SugarLogger.Debugf("err := RetailCatUpdate : %v", categoryErr)
continue
}
if v.Code == "" {
VendorCategoryIDMap[v.Name] = parentID
} else {
VendorCategoryIDMap[v.Code] = parentID
}
if v.Children != nil && len(v.Children) != 0 {
for k2, c := range v.Children {
childrenCateId, err := ebaiApi.ShopCategoryCreate(utils.Int2Str(toStore.ID), parentID, c.Name, len(v.Children)-k2)
if err != nil {
globals.SugarLogger.Debugf("err := RetailCatUpdate Children : %v", err)
continue
}
if c.Code == "" {
VendorCategoryIDMap[c.Name] = childrenCateId
} else {
VendorCategoryIDMap[c.Code] = childrenCateId
}
}
}
}
case 3:
i := offSet
for {
// 同步商品
fromFoodList, err1 := fromApi.RetailListAll(fromStore.VendorStoreID, i)
globals.SugarLogger.Debugf("RetailListAll : %d_%d", i, len(fromFoodList))
if len(fromFoodList) == 0 || fromFoodList == nil {
return nil, fmt.Errorf("fromFoodList 为空 %s ,i:= %d", utils.Format4Output(err1, false), i)
}
errList := BatchInitSkuMtwm2EBai(ctx, fromFoodList, ebaiApi, utils.Int2Str(toStore.ID), VendorCategoryIDMap)
if errList != nil {
for _, err2 := range errList {
errListData = append(errListData, err2.Error())
}
globals.SugarLogger.Debugf("BatchInitData : %s", utils.Format4Output(errList, false))
}
i = i + 1
}
}
return
}
task := tasksch.NewParallelTask(taskName, config, ctx, work, []int{1, 2, 3})
tasksch.HandleTask(task, nil, true).Run()
if !isAsync {
_, err = task.GetResult(0)
hint = "1"
} else {
hint = task.ID
}
return hint, errListData, err
}
// BatchInitSkuMtwm2EBai 批量创建商品美团到饿百
func BatchInitSkuMtwm2EBai(ctx *jxcontext.Context, fromSku []*mtwmapi.AppFood, toApi *ebaiapi.API, storeID string, VendorCategoryIDMap map[string]int64) []error {
var errList = make([]error, 0, 0)
for k, storeSku := range fromSku {
params := map[string]interface{}{}
photos := []map[string]interface{}{}
for _, img := range storeSku.PictureList {
imgEbai, _ := api.EbaiAPI.PictureUpload(img, nil)
if k == 0 {
photos = append(photos, map[string]interface{}{
"is_master": 1,
"url": imgEbai,
})
} else {
photos = append(photos, map[string]interface{}{
"is_master": 0,
"url": imgEbai,
})
}
}
params["photos"] = photos
params["weight"] = storeSku.SkuList[0].Weight
if storeSku.SkuList[0].Weight == "" || utils.Str2Int(storeSku.SkuList[0].Weight) <= 0 {
params["weight"] = "1"
}
params["upc"] = storeSku.SkuList[0].Upc
params["name"] = storeSku.Name
if storeSku.CategoryName != "" {
params["category_name"] = storeSku.CategoryName
}
if storeSku.SecondaryCategoryName != "" {
params["category_name"] = storeSku.SecondaryCategoryName
}
//params["cat3_id"] = storeSku.CateId
//if storeSku.CategoryCode != "" {
// params["category_id"] = VendorCategoryIDMap[storeSku.CategoryCode]
//} else {
// params["category_id"] = VendorCategoryIDMap[storeSku.CategoryName]
//}
if storeSku.PictureContents != "" {
params["desc"] = storeSku.PictureContents
}
params["left_num"] = storeSku.SkuList[0].Stock
params["process_type"] = 0 // 是否支持加工服务0-不支持/1-支持
//params["process_detail"] = storeSku.ProcessDetail
params["sale_price"] = jxutils.StandardPrice2Int(utils.Str2Float64(storeSku.SkuList[0].Price))
if storeSku.IsSoldOut == 1 {
params["status"] = 0
} else {
params["status"] = 1
}
//params["minimum"] = storeSku.Minimum
//params["seven_days_no_reason"] = storeSku.SevenDaysNoReason
//if len(storeSku.SkuProperty) != model.NO {
// params["sku_property"] = storeSku.SkuProperty
//}
var customSkuID string = ""
if storeSku.AppFoodCode != "" {
customSkuID = storeSku.AppFoodCode
} else {
customSkuID = fmt.Sprintf("%d_%d", storeSku.Ctime, k)
}
_, err := toApi.SkuCreate2(ctx.GetTrackInfo(), storeID, customSkuID, params)
if err != nil {
globals.SugarLogger.Debugf("-------erbaierr := %v", err)
errList = append(errList, err)
}
}
return errList
}
//#endregion
//#region 饿百同步到美团
func CopyEBaiToMt(ctx *jxcontext.Context, fromStore, toStore *dao.StoreDetail, isAsync bool, offSet int) (hint string, errList []string, err error) {
var api = api.EbaiAPI
var toApi *mtwmapi.API
if toStore.VendorOrgCode == globals.Mtwm2Code {
toApi = mtwmapi.New(beego.AppConfig.DefaultString("mtwmAppID2", ""), beego.AppConfig.DefaultString("mtwmSecret2", ""), beego.AppConfig.DefaultString("mtwmCallbackURL2", ""), "")
toApi.SetToken(toStore.MtwmToken)
} else {
toApi = partner.CurAPIManager.GetAPI(model.VendorIDMTWM, toStore.VendorOrgCode).(*mtwmapi.API)
}
taskName := fmt.Sprintf("将美团平台门店[%s],分类和商品复制到[%s]", fromStore.VendorStoreID, toStore.VendorStoreID)
config := tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(false)
work := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
step := batchItemList[0].(int)
switch step {
case 1:
//同步分类
fromCategoryList, err := api.ShopCategoryGet(utils.Int2Str(fromStore.ID))
if err != nil {
return nil, err
}
for _, v := range fromCategoryList {
categoryErr := toApi.RetailCatUpdate(toStore.VendorStoreID, v.Name, &mtwmapi.Param4UpdateCat{
CategoryCode: utils.Int64ToStr(v.CategoryID),
Sequence: v.Rank,
})
if categoryErr != nil {
globals.SugarLogger.Debugf("err := RetailCatUpdate : %v", categoryErr)
}
if v.Children != nil && len(v.Children) != 0 {
for _, c := range v.Children {
if err3 := toApi.RetailCatUpdate(toStore.VendorStoreID, v.Name, &mtwmapi.Param4UpdateCat{
CategoryNameOrigin: v.Name,
SecondaryCategoryCode: utils.Int64ToStr(c.CategoryID),
SecondaryCategoryName: c.Name,
Sequence: c.Rank,
}); err3 != nil {
globals.SugarLogger.Debugf("err := RetailCatUpdate Children : %v", err3)
}
}
}
}
case 2:
i := offSet
for {
// 同步商品
fromFoodList, err1 := api.SkuList(utils.Int2Str(fromStore.ID), &ebaiapi.SkuListParams{
Page: i,
})
if len(fromFoodList.List) == 0 || fromFoodList == nil {
return nil, fmt.Errorf("fromFoodList 为空 %s ,i:= %d", utils.Format4Output(err1, false), i)
}
errDataList, err := BatchInitSkuEBai2Mt(ctx, fromFoodList.List, toApi, toStore.VendorStoreID, i)
if err != nil {
globals.SugarLogger.Debugf("BatchInitData : %v", err)
}
if len(errDataList) > model.NO {
globals.SugarLogger.Debugf("errListData %d:= %s", i, utils.Format4Output(errDataList, false))
for _, v := range errDataList {
errList = append(errList, fmt.Sprintf("%s,%s", v.AppFoodCode, v.ErrorMsg))
}
}
globals.SugarLogger.Debugf("==========页数[%d],数据长度[%d]", i, len(fromFoodList.List))
if len(fromFoodList.List) < 100 {
break
}
i++
}
}
return
}
task := tasksch.NewParallelTask(taskName, config, ctx, work, []int{1, 2})
tasksch.HandleTask(task, nil, true).Run()
if !isAsync {
_, err = task.GetResult(0)
hint = "1"
} else {
hint = task.ID
}
globals.SugarLogger.Debugf("======errrList := %s", utils.Format4Output(errList, false))
return hint, errList, err
}
func BatchInitSkuEBai2Mt(ctx *jxcontext.Context, fromSku []*ebaiapi.SkuInfo, toApi *mtwmapi.API, vendorStoreID string, offset int) ([]*mtwmapi.AppFoodResult, error) {
errList := make([]*mtwmapi.AppFoodResult, 0, 0)
foodDataList := make([]map[string]interface{}, len(fromSku))
isNeedUpdatePrice := true
for i, storeSku := range fromSku {
foodData := make(map[string]interface{})
foodDataList[i] = foodData
if storeSku.CustomSkuId != "" {
foodData[mtwmapi.KeyAppFoodCode] = storeSku.CustomSkuId
} else {
foodData[mtwmapi.KeyAppFoodCode] = storeSku.SkuId
}
skus := []map[string]interface{}{
map[string]interface{}{
"sku_id": foodData[mtwmapi.KeyAppFoodCode],
},
}
foodData["skus"] = skus
foodData["name"] = utils.LimitUTF8StringLen(storeSku.Name, mtwmapi.MaxSkuNameCharCount)
//foodData["description"] = storeSku.Comment
if isNeedUpdatePrice {
foodData["price"] = jxutils.IntPrice2Standard(int64(storeSku.SalePrice))
}
if storeSku.Minimum != 0 {
foodData["min_order_count"] = storeSku.Minimum
} else {
foodData["min_order_count"] = 1
}
foodData["unit"] = "份"
//todo 增加商品必填属性
attr := mtwm.SwitchAttr(toApi, vendorStoreID, 0, 0, storeSku.Name)
if attr != "" {
foodData["common_attr_value"] = attr
}
//if storeSku.SellPoint != "" {// 卖点
// foodData["sell_point"] = storeSku.SellPoint
//}
//if storeSku.SellPointTimes != "" {// 卖点展示期
// foodData["sell_point_available_times"] = storeSku.SellPointTimes
//}
if storeSku.CateId2 != 0 {
foodData["category_code"] = storeSku.CateId2
} else {
foodData["category_name"] = storeSku.CateName2
}
if storeSku.Status == "" {
foodData["is_sold_out"] = 0
} else {
foodData["is_sold_out"] = 1
}
photos := make([]string, 0, len(storeSku.Photos))
for _, v := range storeSku.Photos {
photos = append(photos, v.Url)
}
foodData["picture"] = strings.Join(photos, ",")
if storeSku.Rtf != "" {
foodData["picture_contents"] = storeSku.Rtf
}
//if storeSku.QuaPictures != "" {
// foodData["qua_pictures"] = storeSku.QuaPictures
// foodData["qua_effective_date"] = storeSku.QuaEffectiveDate
// foodData["qua_approval_date"] = storeSku.QuaApprovalDate
//}
// 周期性可售时间段
//if storeSku.StatusSaleBegin != model.NO && storeSku.StatusSaleEnd != model.NO {
// saleStart := utils.Int2Str(int(storeSku.StatusSaleBegin))
// saleEnd := utils.Int2Str(int(storeSku.StatusSaleEnd))
// for {
// if len(saleStart) != 4 {
// saleStart = "0" + saleStart
// }
// if len(saleEnd) != 4 {
// saleEnd += "0" + saleEnd
// }
// if len(saleEnd) == 4 && len(saleStart) == 4 {
// break
// }
// }
// saleStart = fmt.Sprintf("%s:%s", saleStart[:2], saleStart[2:])
// saleEnd = fmt.Sprintf("%s:%s", saleEnd[:2], saleEnd[2:])
// availableTimes := fmt.Sprintf("%s-%s", saleStart, saleEnd)
// available, _ := json.Marshal(map[string]string{"monday": availableTimes, "tuesday": availableTimes, "wednesday": availableTimes, "thursday": availableTimes, "friday": availableTimes, "saturday": availableTimes, "sunday": availableTimes})
// foodData["available_times"] = string(available)
//}
//foodData["sequence"] = storeSku.GetSeq()
if tempCateId, err := toApi.RetailRecommendTag(storeSku.Name, vendorStoreID, 0, mtwmapi.TypeCategory); err == nil {
foodData["tag_id"] = int64(tempCateId.TagID)
}
//skus[0]["spec"] = jxutils.ComposeSkuSpec(storeSku.SpecQuality, storeSku.SpecUnit)
skus[0]["price"] = utils.Float64ToStr(jxutils.IntPrice2Standard(int64(storeSku.SalePrice)))
skus[0]["stock"] = utils.Int2Str(storeSku.LeftNum)
skus[0]["upc"] = storeSku.Upc
if storeSku.ShelfNumber != "" {
skus[0]["location_code"] = storeSku.ShelfNumber
}
//skus[0]["ladder_box_num"] = "0"
//skus[0]["ladder_box_price"] = "0"
// 下面这个两个和上面有点重复,但是上面两个在更新的时候美团不识别,不知道创建的时候会不会覆盖一下吧(更新只能用下面这个)
skus[0]["box_num"] = "0"
skus[0]["box_price"] = "0"
if foodData["tag_id"] != nil {
skus[0]["weight"] = storeSku.Weight // weight字段仅限服饰鞋帽、美妆、日用品、母婴、生鲜果蔬、生活超市下的便利店/超市门店品类的商家使用
}
}
count := len(foodDataList) / 10
if len(foodDataList)%10 != 0 {
count += 1
}
for i := 0; i < count; i++ {
if i == count-1 {
failedFoodList, _ := toApi.RetailBatchInitData(ctx.GetTrackInfo(), vendorStoreID, foodDataList[i*10:])
if len(failedFoodList) != 0 {
globals.SugarLogger.Debugf("failedFoodList := %s", utils.Format4Output(failedFoodList, false))
errList = append(errList, failedFoodList...)
}
} else {
failedFoodList, _ := toApi.RetailBatchInitData(ctx.GetTrackInfo(), vendorStoreID, foodDataList[i*10:(i+1)*10])
if len(failedFoodList) != 0 {
globals.SugarLogger.Debugf("failedFoodList := %s", utils.Format4Output(failedFoodList, false))
errList = append(errList, failedFoodList...)
}
}
}
return errList, nil
}
//#endregion
//#region 饿百复制到京东
func CopyEBaiToJd(ctx *jxcontext.Context, fromStore, toStore *dao.StoreDetail, isAsync bool, offSet int) (hint string, errList []string, err error) {
var api = api.EbaiAPI
var toApi *jdapi.API = partner.CurAPIManager.GetAPI(model.VendorIDJD, toStore.VendorOrgCode).(*jdapi.API)
taskName := fmt.Sprintf("将饿百平台门店[%s],分类和商品复制到[%s]", fromStore.VendorStoreID, toStore.VendorStoreID)
config := tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(false)
work := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
step := batchItemList[0].(int)
switch step {
case 1:
// 京东商品分类不做同步
//fromCategoryList, err := api.ShopCategoryGet(utils.Int2Str(fromStore.ID))
//if err != nil {
// return nil, err
//}
//for _, v := range fromCategoryList {
// categoryErr := toApi.RetailCatUpdate(toStore.VendorStoreID, v.Name, &mtwmapi.Param4UpdateCat{
// CategoryCode: utils.Int64ToStr(v.CategoryID),
// Sequence: v.Rank,
// })
// if categoryErr != nil {
// globals.SugarLogger.Debugf("err := RetailCatUpdate : %v", categoryErr)
// }
// if v.Children != nil && len(v.Children) != 0 {
// for _, c := range v.Children {
// if err3 := toApi.RetailCatUpdate(toStore.VendorStoreID, v.Name, &mtwmapi.Param4UpdateCat{
// CategoryNameOrigin: v.Name,
// SecondaryCategoryCode: utils.Int64ToStr(c.CategoryID),
// SecondaryCategoryName: c.Name,
// Sequence: c.Rank,
// }); err3 != nil {
// globals.SugarLogger.Debugf("err := RetailCatUpdate Children : %v", err3)
// }
// }
// }
//}
case 2:
i := offSet
for {
// 同步商品
fromFoodList, err1 := api.SkuList(utils.Int2Str(fromStore.ID), &ebaiapi.SkuListParams{
Page: i,
})
if len(fromFoodList.List) == 0 || fromFoodList == nil {
return nil, fmt.Errorf("fromFoodList 为空 %s ,i:= %d", utils.Format4Output(err1, false), i)
}
err := BatchInitSkuEBai2Jd(ctx, fromFoodList.List, toApi, toStore.VendorStoreID, i)
if err != nil {
globals.SugarLogger.Debugf("BatchInitData : %v", err)
}
globals.SugarLogger.Debugf("==========页数[%d],数据长度[%d]", i, len(fromFoodList.List))
if len(fromFoodList.List) < 100 {
break
}
i++
}
}
return
}
task := tasksch.NewParallelTask(taskName, config, ctx, work, []int{1, 2})
tasksch.HandleTask(task, nil, true).Run()
if !isAsync {
_, err = task.GetResult(0)
hint = "1"
} else {
hint = task.ID
}
globals.SugarLogger.Debugf("======errrList := %s", utils.Format4Output(errList, false))
return hint, errList, err
}
func BatchInitSkuEBai2Jd(ctx *jxcontext.Context, fromSku []*ebaiapi.SkuInfo, toApi *jdapi.API, vendorStoreID string, offset int) []error {
errList := make([]error, 0, 0)
for _, storeSku := range fromSku {
param := &jdapi.OpSkuParam{
TraceID: ctx.GetTrackInfo(),
//ShopCategories: []int64{},
//CategoryID: utils.Str2Int64(catMaps[0].JdID),
CategoryID: 291, // 暂无分类
BrandID: 35247,
SkuName: storeSku.Name,
SkuPrice: storeSku.SalePrice,
Weight: float64(jxutils.IntWeight2Float(utils.Str2Int(storeSku.Weight))),
FixedStatus: 1,
IsSale: -1,
Upc: storeSku.Upc,
}
if storeSku.CustomSkuId != "" {
param.OutSkuID = storeSku.CustomSkuId
} else {
param.OutSkuID = utils.Int64ToStr(storeSku.SkuId)
}
photos := make([]string, 0, len(storeSku.Photos))
for _, v := range storeSku.Photos {
photos = append(photos, v.Url)
}
param.Images = photos
//param.ShopCategories = append(param.ShopCategories, 16578930) // 暂无分类
if storeSku.Rtf != "" {
param.ProductDesc = fmt.Sprintf(`<img src="%s" alt="一张图片" />`, storeSku.Rtf)
}
skuID, err := toApi.AddSku2(param)
if err != nil {
errList = append(errList, err)
}
if skuID != "" {
toApi.UpdateCurrentQty(ctx.GetTrackInfo(), vendorStoreID, utils.Str2Int64(skuID), storeSku.LeftNum)
SaleStatus := []*jdapi.QueryStockRequest{
&jdapi.QueryStockRequest{
StationNo: vendorStoreID,
SkuId: utils.Str2Int64(skuID),
//DoSale: mtSku.IsSoldOut,
},
}
if storeSku.Status == "1" {
SaleStatus[0].DoSale = 0
} else {
SaleStatus[0].DoSale = 1
}
toApi.UpdateVendibility(ctx.GetTrackInfo(), SaleStatus)
}
}
return errList
}
//#endregion
////#region 同步活动
//
//func CopyMtActToMt(ctx *jxcontext.Context, fromStore, toStore *dao.StoreDetail) {
// var fromApi, toApi *mtwmapi.API
// var errData = make([]error, 0, 0)
//
// if fromStore.VendorOrgCode == globals.Mtwm2Code {
// fromApi = mtwmapi.New(beego.AppConfig.DefaultString("mtwmAppID2", ""), beego.AppConfig.DefaultString("mtwmSecret2", ""), beego.AppConfig.DefaultString("mtwmCallbackURL2", ""), "")
// fromApi.SetToken(fromStore.MtwmToken)
// } else {
// fromApi = partner.CurAPIManager.GetAPI(model.VendorIDMTWM, fromStore.VendorOrgCode).(*mtwmapi.API)
// }
//
// if toStore.VendorOrgCode == globals.Mtwm2Code {
// toApi = mtwmapi.New(beego.AppConfig.DefaultString("mtwmAppID2", ""), beego.AppConfig.DefaultString("mtwmSecret2", ""), beego.AppConfig.DefaultString("mtwmCallbackURL2", ""), "")
// toApi.SetToken(toStore.MtwmToken)
// } else {
// toApi = partner.CurAPIManager.GetAPI(model.VendorIDMTWM, toStore.VendorOrgCode).(*mtwmapi.API)
// }
//
// // 买赠活动
// buyGiftList, err := fromApi.BatchQueryBuyGiftsAll(fromStore.VendorStoreID)
// if err != nil {
// errData = append(errData, fmt.Errorf("买赠活动异常:%v", errData))
// }
// if len(buyGiftList) == model.NO {
// errData = append(errData, fmt.Errorf("门店暂无买赠活动"))
// }
//
// param := make([]*mtwmapi.BatchCreateBuyGiftsParam, 0, 0)
// for _, v := range buyGiftList {
// buyGift := &mtwmapi.BatchCreateBuyGiftsParam{
// AppSpuCode: v.AppSpuCode,
// StartTime: v.StartTime,
// EndTime: v.EndTime,
// GiftsType: v.GiftsType,
// GiftsName: v.GiftsName,
// GiftsAppSpuCode: v.GiftsAppSpuCode,
// BuyNum: v.BuyNum,
// GiftsNum: v.GiftsNum,
// GiftsCharge: v.GiftsCharge,
// GiftsDayLimit: v.GiftsDayLimit,
// }
//
// // 赠品成本
// giftsCharge := &mtwmapi.GiftsChargeObj{}
// if err := json.Unmarshal([]byte(v.Charge), giftsCharge); err != nil {
// return
// }
//
// buyGift.GiftsCharge = utils.Str2Float64(fmt.Sprintf("%.2f", utils.Str2Float64(giftsCharge.GiftsCharge)))
// param = append(param, buyGift)
// }
// toApi.BatchCreateBuyGifts(toStore.VendorStoreID, param)
//
//}
//
//// SyncStoreFullReduction 同步门店满减活动
//func SyncStoreFullReduction(fromStoreApi, toStoreApi *mtwmapi.API, fromStoreId, toStoreId string) error {
// data, err := fromStoreApi.BatchGetDiscountList(fromStoreId, 0)
// if err != nil {
// return err
// }
//
// errList := make([]string, 0.0)
// for k, v := range data {
// param := &mtwmapi.CreateDiscountList{
// AppPoiCode: toStoreId,
// ActInfo: "",
// ActDetails: "",
// AppFoods: "",
// }
// actInfo := &mtwmapi.CreateDisCountActInfo{
// StartTime: v.ActInfo.StartTime,
// EndTime: v.ActInfo.EndTime,
// ActType: 0, // 0 不传app_food
// AutoDelay: 1,
// }
// if v.ActInfo.ActName != "" {
// actInfo.ActName = v.ActInfo.ActName
// } else {
// actInfo.ActName = "全店满减" + utils.Int2Str(k)
// }
//
// actInfoByte, err := json.Marshal(actInfo)
// if err != nil {
// return err
// }
// param.ActInfo = string(actInfoByte)
//
// actDetails := make([]mtwmapi.CreateDiscountActDetails, 0, 0)
// for _, ads := range v.ActDetails {
// actDetails = append(actDetails, mtwmapi.CreateDiscountActDetails{
// OriginPrice: utils.Float64TwoInt(ads.OriginPrice),
// ActPrice: utils.Float64TwoInt(ads.ActPrice),
// })
// }
// actDetailsByte, err := json.Marshal(actDetails)
// if err != nil {
// return err
// }
// param.ActDetails = string(actDetailsByte)
// _, err = toStoreApi.BatchCreateDiscountList(param)
// if err != nil {
// errList = append(errList, actInfo.ActName+":"+err.Error())
// }
// }
//
// return fmt.Errorf(strings.Join(errList, ","))
//}
//
//// SyncStoreShippingFee 同步门店运费满减活动
//func SyncStoreShippingFee(fromStoreApi, toStoreApi *mtwmapi.API, fromStoreId, toStoreId string) error {
// data, err := fromStoreApi.BatchGetShippingFee(fromStoreId)
// if err != nil {
// return err
// }
//
// if data.SuccessList != nil && len(data.SuccessList) != 0 {
// param := make([]*mtwmapi.ShippingFeeBatchCreateActData, 0, 0)
// for _, v := range data.SuccessList {
// shippingFee := &mtwmapi.ShippingFeeBatchCreateActData{
// StartTime: v.StartTime,
// EndTime: v.EndTime,
// WeeksTime: v.WeeksTime,
// Period: v.Period,
// MaxPrice: v.MaxPrice,
// }
// actDetail := make([]mtwmapi.ShippingFeeBatchCreateActDataActDetail, 0, 0)
// for _, ad := range v.ActDetail {
// actDetail = append(actDetail, mtwmapi.ShippingFeeBatchCreateActDataActDetail{
// LimitPrice: ad.LimitPrice,
// DiscountPrice: ad.DiscountPrice,
// })
// }
// shippingFee.ActDetail = actDetail
//
// param = append(param, shippingFee)
// }
//
// resultData, err := toStoreApi.BatchCreateShippingFee(toStoreId, param)
// if err != nil {
// return err
// }
// if resultData.Data != "ok" {
// return fmt.Errorf(resultData.Msg)
// }
// }
// return nil
//}
//
//// SyncStoreProducts 同步门店折扣爆品活动
//func SyncStoreProducts(fromStoreApi, toStoreApi *mtwmapi.API, fromStoreId, toStoreId string) error {
// secKillALL, err := fromStoreApi.QueryProductsAll(fromStoreId, mtwmapi.RetailActTypeSecKill)
// if err != nil {
// return err
// }
//
// killAct := make([]*mtwmapi.RetailDiscountActData, 0, 0)
// for _, v := range secKillALL {
// kill := &mtwmapi.RetailDiscountActData{
// AppFoodCode: v.AppFoodCode,
// UserType: v.UserType,
// StartTime: v.StartTime,
// EndTime: v.EndTime,
// OrderLimit: v.OrderLimit,
// DayLimit: v.DayLimit,
// Period: v.Period,
// WeeksTime: v.WeeksTime,
// SettingType: v.SettingType,
// ActPrice: v.ActPrice,
// DiscountCoefficient: v.DiscountCoefficient,
// Sequence: v.Sequence,
// ItemID: v.ItemID,
// OriginalPrice: v.OriginalPrice,
// Stock: v.Stock,
// Status: v.Status,
// Name: v.Name,
// }
// killAct = append(killAct, kill)
// }
//
// //todo 折扣活动查询出来一万多条,暂时工单处理
// directDownALL, err := fromStoreApi.QueryProductsAll(fromStoreId, mtwmapi.RetailActTypeDirectDown)
// for _, v := range directDownALL {
// kill := &mtwmapi.RetailDiscountActData{
// AppFoodCode: v.AppFoodCode,
// UserType: v.UserType,
// StartTime: v.StartTime,
// EndTime: v.EndTime,
// OrderLimit: v.OrderLimit,
// DayLimit: v.DayLimit,
// Period: v.Period,
// WeeksTime: v.WeeksTime,
// SettingType: v.SettingType,
// ActPrice: v.ActPrice,
// DiscountCoefficient: v.DiscountCoefficient,
// Sequence: v.Sequence,
// ItemID: v.ItemID,
// OriginalPrice: v.OriginalPrice,
// Stock: v.Stock,
// Status: v.Status,
// Name: v.Name,
// }
// killAct = append(killAct, kill)
// }
// if len(killAct) != 0 {
// _, _, err := toStoreApi.CreateProductsAct(toStoreId, mtwmapi.RetailActTypeSecKill, killAct)
// if err != nil {
// return err
// }
// }
//
// return nil
//}
//
//// SyncStoreCoupon 同步门店商家券(单店店内商家券)
//func SyncStoreCoupon(fromStoreApi, toStoreApi *mtwmapi.API, fromStoreId, toStoreId string) error {
// couponList, err := fromStoreApi.QueryStoreCoupon(fromStoreId)
// if err != nil {
// return err
// }
//
// errList := make([]string, 0, 0)
// if len(couponList) != 0 {
// for _, v := range couponList {
// couponInfoList := make([]*mtwmapi.CouponInfo, 0, 0)
// for _, ad := range v.ActData {
// couponInfoList = append(couponInfoList, &mtwmapi.CouponInfo{
// LimitPrice: ad.LimitPrice,
// CouponPrice: ad.CouponPrice,
// UserType: ad.UserType,
// ValidityDays: ad.ValidityDays,
// Stock: ad.Stock,
// })
// }
//
// limitTime := &mtwmapi.LimitTime{
// StartTime: v.StartTime,
// EndTime: v.EndTime,
// }
//
// if _, err := toStoreApi.CreateStoreCouponBatch(toStoreId, limitTime, couponInfoList); err != nil {
// errList = append(errList, err.Error())
// }
// }
// }
//
// if len(errList) != 0 {
// return fmt.Errorf(strings.Join(errList, ","))
// }
// return nil
//}
//
//// SyncStoreXDiscount 同步门店X件优惠活动(x件m元/X件m折/X件减m元)目前只有x件m元,其他两个查询不出来,美团创建成功也查询不出来
//// 暂时不能创建,无法获取商品的活动价格和活动库存
//func SyncStoreXDiscount(fromStoreApi, toStoreApi *mtwmapi.API, fromStoreId, toStoreId string) error {
// //result := make([]*mtwmapi.BundlesSuccessListDetail, 0, 0)
// //// X件M元
// //bundlesXM, _ := fromStoreApi.QueryBundlesAct(fromStoreId, 0)
// //if bundlesXM != nil {
// // result = append(result, bundlesXM...)
// //}
// //// X件Y折
// //bundlesXY, _ := fromStoreApi.QueryBundlesAct(fromStoreId, 2)
// //if bundlesXY != nil {
// // result = append(result, bundlesXY...)
// //}
// //
// //errList := make([]error, 0, 0)
// //for _, v := range result {
// // actSku, err := fromStoreApi.QueryBundlesSku(fromStoreId, utils.Int64ToStr(v.ActId))
// // if err != nil {
// // errList = append(errList, err)
// // continue
// // }
// //
// //}
//
// return nil
//}
//
//// SyncRepurchaseSku 加价购[无法获取活动商品的价格和库存暂不创建]
//func SyncRepurchaseSku() {
// return
//}
//
//// SyncStoreCouponAct 门店商品券活动
//func SyncStoreCouponAct(fromStoreApi, toStoreApi *mtwmapi.API, fromStoreId, toStoreId string) error {
// couponInfoAll := make([]*mtwmapi.GetStoreCouponActSuccessList, 0, 0)
// pageNumber := 1
// pageSize := 100
// for {
// param := &mtwmapi.GetStoreCouponActParam{
// AppPoiCode: fromStoreId,
// ActStatus: 1,
// StartTime: time.Now().AddDate(-1, 0, 0).Unix(),
// EndTime: time.Now().AddDate(1, 0, 0).Unix(),
// PageNum: pageNumber,
// PageSize: pageSize,
// }
//
// couponList, err := fromStoreApi.GetStoreCouponAct(param)
// if err != nil {
// return err
// }
// couponInfoAll = append(couponInfoAll, couponList...)
// if len(couponList) < pageSize {
// break
// }
// pageNumber++
// }
//
// for _, v := range couponInfoAll {
// couponSkuList, err := fromStoreApi.GetCouponActSkuList(fromStoreId, utils.Int64ToStr(v.ActId))
// if err != nil {
// return err
// }
//
// for _, csl := range couponSkuList {
// param := &mtwmapi.CreateCouponAct{
// AppPoiCodes: csl.AppPoiCodes,
// CouponName: csl.CouponName,
// IsSinglePoi: csl.IsSinglePoi,
// AppSpuCodes: csl.AppSpuCodes,
// TakeCouponStartTime: csl.TakeCouponStartTime,
// TakeCouponEndTime: csl.TakeCouponEndTime,
// UseCouponStartTime: csl.UseCouponStartTime,
// CouponLimitCount: csl.CouponLimitCount,
// Type: csl.Type,
// }
//
// if csl.SpuData != nil && len(csl.SpuData) != 0 {
// for _, sd := range csl.SpuData {
// param.SpuData = append(param.SpuData, mtwmapi.CreateCouponActSpuData{
// AppSpuCode: sd.AppSpuCode,
// SkuId: sd.SkuId,
// Upc: sd.Upc,
// })
// }
// }
//
// if csl.ActPriceCouponInfo != nil && len(csl.ActPriceCouponInfo) != 0 {
// param.ActPriceCouponInfo = csl.ActPriceCouponInfo
// }
// if csl.DiscountCouponInfo != nil && len(csl.DiscountCouponInfo) != 0 {
// param.DiscountCouponInfo = csl.DiscountCouponInfo
// }
//
// _, err = toStoreApi.CreateCouponAct(param)
// if err != nil {
// return err
// }
// }
// }
//
// return nil
//}
//
////#endregion