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(`一张图片`, 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