添加美团个各个平台相互复制

This commit is contained in:
邹宗楠
2025-03-17 13:46:20 +08:00
parent ea73635848
commit 8306b04823
7 changed files with 832 additions and 38 deletions

View File

@@ -4,14 +4,19 @@ import (
"encoding/json"
"fmt"
"git.rosy.net.cn/baseapi/platformapi/ebaiapi"
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"
@@ -50,6 +55,14 @@ func CopyOnStoreSkuToOther(ctx *jxcontext.Context, fromStoreId, toStoreId string
_, 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 10: // 美团到京东
default:
return nil, fmt.Errorf("暂时还不支持")
}
@@ -64,7 +77,7 @@ func CopyOnStoreSkuToOther(ctx *jxcontext.Context, fromStoreId, toStoreId string
return copySkuErr, err
}
//#region 同步商品
//#region 饿百同步商品
// CopyEBaiToEBai 饿了么商品复制到饿了么
func CopyEBaiToEBai(ctx *jxcontext.Context, fromStore, toStore *dao.StoreDetail, isAsync bool, offset int) (hint string, errList []string, err error) {
@@ -180,10 +193,14 @@ func BatchInitSkuEBai2EBai(ctx *jxcontext.Context, fromSku []*ebaiapi.SkuInfo, t
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 //= mtwm.GetAPI(fromStore.VendorOrgCode, fromStore.ID, fromStore.VendorStoreID)
var toApi *mtwmapi.API // = mtwm.GetAPI(toStore.VendorOrgCode, toStore.ID, toStore.VendorStoreID)
var fromApi *mtwmapi.API
var toApi *mtwmapi.API
var errList = make([]*mtwmapi.AppFoodResult, 0, 0)
var errData = make([]string, 0, 0)
@@ -207,35 +224,35 @@ func CopyMtToMT(ctx *jxcontext.Context, fromStore, toStore *dao.StoreDetail, isA
switch step {
case 1:
// 同步分类
//fromCategoryList, err := fromApi.RetailCatList(fromStore.VendorStoreID)
//if len(fromCategoryList) == model.NO {
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
//}
////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)
// }
// }
// }
//}
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
@@ -462,6 +479,743 @@ func createCommonAttrValue(apiObj *mtwmapi.API, tempCatID int64, name string) (s
//#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: tiktok_api.TiktokPayType1,
ReduceType: tiktok_api.SkuReduceTypePayMakeOrder,
DeliveryDelayDay: tiktok_api.DeliveryDelayDayToDay,
PresellType: tiktok_api.SendGoodsTypeNow,
Supply7dayReturn: 0, // 是否支持7天无理由0不支持1支持2支持拆封后不支持
Mobile: toStoreDetail.Tel1,
Commit: true,
//Specs: "重量|" + utils.Float64ToStr(float64(storeSku.SpecQuality)) + storeSku.SpecUnit,
NeedRechargeMode: false,
SellChannel: []int64{0},
StartSaleType: 0,
PickupMethod: "0",
OuterProductId: 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.Str2Float64(sl.Weight)
upc = sl.Upc
}
param.Specs = 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 = 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
}
// 是否支持七天无理由
if toApi.GetProductUpdateRule(param.CategoryLeafId) {
param.Supply7dayReturn = 1
} else {
param.Supply7dayReturn = 0
}
// weight_unit 目前抖音只支持g和kg两种
param.WeightUnit = 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 = string(data)
// 获取商品的属性
//param.ProductFormatNew, param.StandardBrandId, err = MakeProductFormatNew(api, int64(storeSku.NameID), param.CategoryLeafId, storeSku.Upc, storeSku.UpcBrandName, storeSku.UpcTiktokBrandId)
if upc == "" {
param.StandardBrandId = 596120136
} else {
brandName, err := tiktok_store.GetBrandByBrandName(upc)
if err != nil {
param.StandardBrandId = 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 = 596120136
} else {
param.StandardBrandId = standardBrandId
}
}
}
if param.StandardBrandId == 0 {
param.StandardBrandId = 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.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.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
}
// 获取门店限售模板
param.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
}
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)
} 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 _, c := range v.Children {
childrenCateId, err := ebaiApi.ShopCategoryCreate(utils.Int2Str(toStore.ID), parentID, c.Name, len(v.Children)-k)
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)
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
params["weight"] = storeSku.SkuList[0].Weight
params["upc"] = storeSku.SkuList[0].Upc
params["name"] = storeSku.Name
//params["cat3_id"] = storeSku.CateId
if storeSku.CategoryCode != "" {
params["category_id"] = VendorCategoryIDMap[storeSku.CategoryCode]
} else {
params["category_id"] = VendorCategoryIDMap[storeSku.Name]
}
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
//}
customSkuID := int64(storeSku.Ctime + k)
_, err := toApi.SkuCreate(ctx.GetTrackInfo(), storeID, customSkuID, params)
if err != nil {
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
//#endregion
////#region 同步活动
//
//func CopyMtActToMt(ctx *jxcontext.Context, fromStore, toStore *dao.StoreDetail) {

View File

@@ -672,6 +672,7 @@ func AddUserDeliveryAddress(ctx *jxcontext.Context, address *model.UserDeliveryA
if address.UserID == "" {
return nil, fmt.Errorf("操作用户配送地址时必须指定UserID")
}
db := dao.GetDB()
lng := address.Lng
lat := address.Lat

View File

@@ -41,9 +41,10 @@ func SendFilesToStores(ctx *jxcontext.Context, files []*multipart.FileHeader, ti
return "", fmt.Errorf("文件名:%s不规范没有包含三个必要的部分", fileHeader.Filename)
}
fileList[k].StoreID = int(utils.Str2Int64WithDefault(fileNameParts[0], 0))
if fileList[k].StoreID < 100000 || fileList[k].StoreID > 1000000 {
return "", fmt.Errorf("文件名:%s不规范不以合法的京西门店ID开始", fileHeader.Filename)
}
// 果园和菜市账号冲突,果园门店ID调整变大
//if fileList[k].StoreID < 100000 || fileList[k].StoreID > 1000000 {
// return "", fmt.Errorf("文件名:%s不规范不以合法的京西门店ID开始", fileHeader.Filename)
//}
}
putPolicy := storage.PutPolicy{
Scope: globals.QiniuBucket,

View File

@@ -14,3 +14,19 @@ func GetVendorImg(skuId, vendorId int) (*model.TaoSkuImg, error) {
return result, nil
}
func GetCopyInfo(fromVendorStoreId string, fromVendorId int, toVendorStoreID string, toVendorId int) (map[string]*model.CopyVendorSku, error) {
result := make([]*model.CopyVendorSku, 0, 0)
sql := ` SELECT * FROM copy_vendor_sku WHERE from_store_id = ? AND from_vendor_id = ? AND to_store_id = ? AND to_vendor_id = ?`
if err := GetRows(GetDB(), &result, sql, []interface{}{fromVendorStoreId, fromVendorId, toVendorStoreID, toVendorId}); err != nil {
return nil, err
}
data := make(map[string]*model.CopyVendorSku, len(result))
for _, v := range result {
data[v.FromSkuID] = v
}
return data, nil
}

View File

@@ -1,5 +1,6 @@
package model
// 记录图片的
type TaoSkuImg struct {
ID int64 `orm:"column(id)" json:"id"`
SkuID int `orm:"column(sku_id)"`
@@ -17,3 +18,23 @@ func (*TaoSkuImg) TableUnique() [][]string {
[]string{"SkuID", "VendorID"},
}
}
// CopyVendorSku 美团商品复制到抖音
type CopyVendorSku struct {
ID int64 `orm:"column(id)" json:"id"`
FromSkuID string `orm:"column(from_sku_id);size(256)" json:"from_sku_id"` // 来源商品Id
FromSkuName string `orm:"column(from_sku_name);size(512)" json:"from_sku_name"` // 来源商品名称
FromStoreId string `orm:"column(from_store_id);size(64)" json:"from_store_id"` // 来源门店ID
FromVendorId int `orm:"column(from_vendor_id);size(2)" json:"from_vendor_id"` // 来源平台ID
ToStoreId string `orm:"column(to_store_id);size(64)" json:"to_store_id"` // 目标门店ID
ToVendorId int `orm:"column(to_vendor_id);size(2)" json:"to_vendor_id"` // 目标平台
MainSkuId string `orm:"column(main_sku_id);size(128)" json:"main_sku_id"` // 目标平台商品主ID
ChildrenSkuId string `orm:"column(children_sku_id);size(128)" json:"children_sku_id"` // 目标平台商品ID
ErrMsg string `orm:"column(err_msg);size(512)" json:"err_msg"` // 复制错误信息
}
func (*CopyVendorSku) TableUnique() [][]string {
return [][]string{
[]string{"FromStoreId", "FromSkuID"},
}
}

View File

@@ -561,7 +561,7 @@ func getTiktokBrandId(api *tiktokShop.API, db *dao.DaoDB, upc, upcBrandName, upc
dao.UpdateSkuNameTiktokBrandName(db, upc, upcBrandName, utils.Int64ToStr(standardBrandId))
return standardBrandId, nil
} else if upc != "" && upcBrandName == "" && upcTiktokBrandId == "" {
brandName, err := getBrandByBrandName(upc)
brandName, err := GetBrandByBrandName(upc)
if err != nil {
return 0, err
}
@@ -584,7 +584,7 @@ func getTiktokBrandId(api *tiktokShop.API, db *dao.DaoDB, upc, upcBrandName, upc
return 0, errors.New("getTiktokBrandId 获取抖音品牌id异常")
}
func getBrandByBrandName(upcCode string) (string, error) {
func GetBrandByBrandName(upcCode string) (string, error) {
vendorOrgCode := ""
if beego.BConfig.RunMode == model.ServerTypeFruits {
vendorOrgCode = jdapi.JdVendorOrgCodeFruit
@@ -1010,6 +1010,7 @@ func GetTiktokImgList(api *tiktokShop.API, storeId string, skuId int, detailImg,
if strings.Contains(k, "white_") {
whiteTiktok = v.ByteUrl
localTiktokObj.Img = v.ByteUrl
continue
}
tiktokImg = append(tiktokImg, v.ByteUrl)
}

View File

@@ -1051,9 +1051,9 @@ func (c *StoreSkuController) CopyMtToJd() {
// @Title 美团门店复制到美团(相同平台门店相互复制)
// @Description 美团门店复制到美团(相同平台门店相互复制)
// @Param token header string true "认证token"
// @Param fromStoreID formData string true "被复制门店id"
// @Param toStoreID formData string true "复制到门店id"
// @Param vendorID formData int true "平台id"
// @Param fromStoreID formData string true "被复制门店id(平台ID)"
// @Param toStoreID formData string true "复制到门店id(平台ID)"
// @Param vendorID formData int true "平台id[(0京东-京东)(3饿百-饿百)(1美团-美团)(14抖音-抖音)(16淘宝-淘宝)(114美团-抖音)(31饿百-美团)(13美团-饿百)]"
// @Param offSet formData int false "跳过页码"
// @Param syncType formData int true "同步类型[1-商品/2-活动/3-前两者一起同步]"
// @Success 200 {object} controllers.CallResult