package tiktok_store import ( "errors" "fmt" "git.rosy.net.cn/baseapi/platformapi/mtwmapi" sku_editPrice_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/sku_editPrice/request" sku_syncStock_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/sku_syncStock/request" tiktokShop "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/tiktok_api" "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/business/jxutils/tasksch" "git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/business/partner" "git.rosy.net.cn/jx-callback/business/partner/putils" "git.rosy.net.cn/jx-callback/globals" "regexp" ) const ( updateTypeStock = iota updateTypeStatus updateTypePrice ) const ( defVendorCatID = 200001903 // 生菜 specialStoreID = "8171010" // specialStoreID = "2523687" ) var ( sensitiveWordRegexp = regexp.MustCompile(`包含敏感词:(\[.*\])`) ) // GetStoreSkusBatchSize 当前任务最大更新条数 func (p *PurchaseHandler) GetStoreSkusBatchSize(funcID int) (batchSize int) { switch funcID { case partner.FuncUpdateStoreSkusStock, partner.FuncUpdateStoreSkusStatus, partner.FuncUpdateStoreSkusPrice: batchSize = tiktokShop.MaxStoreSkuBatchSize case partner.FuncDeleteStoreSkus: batchSize = tiktokShop.MaxBatchDeleteSize case partner.FuncCreateStoreSkus: batchSize = 1 // 可考虑用批量操作 case partner.FuncUpdateStoreSkus: batchSize = 1 // mtwmapi.MaxStoreSkuBatchSize case partner.FuncGetStoreSkusFullInfo: batchSize = 1 case partner.FuncCreateActs: batchSize = tiktokShop.MaxRetailDiscountCreateBatchSize case partner.FuncCancelActs: batchSize = tiktokShop.MaxRetailDiscountDeleteBatchSize } return batchSize } // GetStoreAllCategories 门店分类 func (p *PurchaseHandler) GetStoreAllCategories(ctx *jxcontext.Context, storeID int, vendorStoreID string) (cats []*partner.BareCategoryInfo, err error) { remoteCats, err := getAPI(getStoreVendorOrgCode(storeID), storeID, vendorStoreID).GetShopCategory(0) if err == nil { cats = convertVendorCatList(remoteCats) } return cats, err } func (p *PurchaseHandler) IsErrCategoryExist(err error) (isExist bool) { return mtwmapi.IsErrCategoryExist(err) } func (p *PurchaseHandler) IsErrCategoryNotExist(err error) (isNotExist bool) { return mtwmapi.IsErrCategoryNotExist(err) } func (p *PurchaseHandler) CreateStoreCategory(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeCat *dao.SkuStoreCatInfo) (err error) { //level := 1 //if storeCat.ParentCatName != "" { // level = 2 //} //originName := "" //catName := storeCat.Name //catCode := storeCat.ID // //subCatName := "" //subCatCode := 0 //if storeCat.CatSyncStatus&model.SyncFlagNewMask == 0 { // // 修改一级分类 // originName = storeCat.VendorCatID //} //if level == 2 { // 二级分类 // // 创建二级分类 // originName = storeCat.ParentVendorCatID // catName = storeCat.ParentCatName // catCode = storeCat.ParentID // subCatName = storeCat.Name // subCatCode = storeCat.ID // if storeCat.CatSyncStatus&model.SyncFlagNewMask == 0 { // // 修改二级分类 // originName = storeCat.VendorCatID // catName = storeCat.Name // catCode = storeCat.ID // subCatName = "" // subCatCode = 0 // } //} //if catName == "" { // panic("catName is empty") //} //catName = utils.FilterEmoji(catName) //subCatName = utils.FilterEmoji(subCatName) // vendorStoreID, originName, catCode, catName, subCatCode, subCatName, storeCat.Seq) //if globals.EnableMtwmStoreWrite { // // err = api.MtwmAPI.RetailCatUpdate2(vendorStoreID, tryCatName2Code(originName), originName, catCode2Str(catCode), catName, catCode2Str(subCatCode), subCatName, storeCat.Seq) // param4Update := &mtwmapi.Param4UpdateCat{ // CategoryCodeOrigin: tryCatName2Code(originName), // CategoryNameOrigin: originName, // CategoryCode: catCode2Str(catCode), // SecondaryCategoryCode: catCode2Str(subCatCode), // SecondaryCategoryName: subCatName, // Sequence: storeCat.Seq, // } // err = getAPI(storeCat.VendorOrgCode, storeID, vendorStoreID).RetailCatUpdate(vendorStoreID, catName, param4Update) // if storeCat.CatSyncStatus&model.SyncFlagNewMask == 0 && p.IsErrCategoryNotExist(err) && originName != "" { // 修改分类名,但分类不存在 // storeCat.CatSyncStatus |= model.SyncFlagNewMask // err = p.CreateStoreCategory(ctx, storeID, vendorStoreID, storeCat) // } //} //if err == nil { // // storeCat.VendorCatID = utils.FilterEmoji(storeCat.Name) // storeCat.VendorCatID = utils.Int2Str(storeCat.ID) //} return err } func (p *PurchaseHandler) UpdateStoreCategory(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeCat *dao.SkuStoreCatInfo) (err error) { return p.CreateStoreCategory(ctx, storeID, vendorStoreID, storeCat) } func (p *PurchaseHandler) DeleteStoreCategory(ctx *jxcontext.Context, storeID int, vendorStoreID, vendorCatID string, level int) (err error) { //if false { // if globals.EnableMtwmStoreWrite { // err = getAPI(getStoreVendorOrgCode(storeID), storeID, vendorStoreID).RetailCatDelete(vendorStoreID, tryCatName2Code(vendorCatID), vendorCatID) // } //} else { // var catCodes []string // if catCode := tryCatName2Code(vendorCatID); catCode != "" { // catCodes = []string{catCode} // } // if globals.EnableMtwmStoreWrite { // if level == 1 { // err = getAPI(getStoreVendorOrgCode(storeID), storeID, vendorStoreID).RetailCatSkuBatchDelete2(ctx.GetTrackInfo(), vendorStoreID, catCodes, []string{vendorCatID}, nil, nil, nil) // } else { // err = getAPI(getStoreVendorOrgCode(storeID), storeID, vendorStoreID).RetailCatSkuBatchDelete2(ctx.GetTrackInfo(), vendorStoreID, nil, nil, catCodes, []string{vendorCatID}, nil) // } // } //} return err } // 多门店平台不需要实现这个接口 func (p *PurchaseHandler) IsErrSkuExist(err error) (isExist bool) { return false } func (p *PurchaseHandler) IsErrSkuNotExist(err error) (isNotExist bool) { return mtwmapi.IsErrSkuNotExist(err) } // func duplicateStoreSkuList(storeSkuList []*dao.StoreSkuSyncInfo, index int) (newStoreSkuList []*dao.StoreSkuSyncInfo) { // newStoreSkuList = make([]*dao.StoreSkuSyncInfo, len(storeSkuList)) // for k, v := range storeSkuList { // tmp := *v // tmp.SkuName = fmt.Sprintf("%s.%d", tmp.SkuName, index) // tmp.SkuID = index*1000000 + tmp.SkuID // newStoreSkuList[k] = &tmp // } // return newStoreSkuList // } func (p *PurchaseHandler) UpdateStoreSkus(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*dao.StoreSkuSyncInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) { failedList, err = p.createOrUpdateStoreSkus(ctx, storeID, vendorStoreID, storeSkuList, false) // if err == nil && vendorStoreID == specialStoreID { // for i := 0; i < 2; i++ { // p.createOrUpdateStoreSkus(ctx, storeID, vendorStoreID, duplicateStoreSkuList(storeSkuList, i+1), true) // } // } return failedList, err } // CreateStoreSkus 门店创建商品 func (p *PurchaseHandler) CreateStoreSkus(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*dao.StoreSkuSyncInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) { failedList, err = p.createOrUpdateStoreSkus(ctx, storeID, vendorStoreID, storeSkuList, true) //if err == nil && vendorStoreID == specialStoreID { // for i := 0; i < 2; i++ { // p.createOrUpdateStoreSkus(ctx, storeID, vendorStoreID, duplicateStoreSkuList(storeSkuList, i+1), true) // } //} return failedList, err } func (p *PurchaseHandler) DeleteStoreSkus(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) { if globals.EnableDdStoreWrite { for _, v := range storeSkuList { if err = getAPI(storeSkuList[0].VendorOrgCode, storeID, vendorStoreID).DeleteStoreCommodity(utils.Str2Int64(v.VendorSkuID)); err != nil { failedList = append(failedList, putils.GetErrMsg2FailedSingleList(v, err, storeID, model.VendorChineseNames[model.VendorIDDD], "删除商品")...) } } } if len(failedList) > 0 { err = nil } return failedList, err } // UpdateStoreSkusStatus 批量更新商品上下架(上架商品库存更新最大,下架商品库存为0) func (p *PurchaseHandler) UpdateStoreSkusStatus(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo, status int) (failedList []*partner.StoreSkuInfoWithErr, err error) { api := getAPI(vendorOrgCode, storeID, vendorStoreID) if status == model.SkuStatusNormal { // 下架 for _, v := range storeSkuList { // 子品商品id获取skuId childrenSku, err := api.GetSkuDetail(v.VendorSkuID, "") if err != nil { return nil, err } if len(childrenSku.SpecPrices) <= 0 { return nil, errors.New("子品商品详情获取失败") } param := &sku_syncStock_request.SkuSyncStockParam{ SkuId: childrenSku.SpecPrices[0].SkuId, ProductId: utils.Str2Int64(v.VendorSkuID), Incremental: false, IdempotentId: "", StockNum: 99999, OutWarehouseId: vendorStoreID, } if err := api.UpdateSkuStock(param); err != nil { failedList = append(failedList, putils.GetErrMsg2FailedSingleList(v, err, storeID, model.VendorChineseNames[model.VendorIDDD], fmt.Sprintf("下架架商品异常,添加固定库存.%s", utils.Format4Output(param, false)))...) } else { failedList = append(failedList, putils.GetErrMsg2FailedSingleList(v, err, storeID, model.VendorChineseNames[model.VendorIDDD], fmt.Sprintf("下架商品异常,添加固定库存.%s", utils.Format4Output(param, false)))...) } } } else { // 上架 for _, v := range storeSkuList { // 子品商品id获取skuId childrenSku, err := api.GetSkuDetail(v.VendorSkuID, "") if err != nil { return nil, err } if len(childrenSku.SpecPrices) <= 0 { return nil, errors.New("子品商品详情获取失败") } param := &sku_syncStock_request.SkuSyncStockParam{ SkuId: childrenSku.SpecPrices[0].SkuId, ProductId: utils.Str2Int64(v.VendorSkuID), Incremental: false, IdempotentId: "", StockNum: 0, OutWarehouseId: vendorStoreID, } if err := api.UpdateSkuStock(param); err != nil { failedList = append(failedList, putils.GetErrMsg2FailedSingleList(v, err, storeID, model.VendorChineseNames[model.VendorIDDD], fmt.Sprintf("上架商品异常,添加固定库存.%s", utils.Format4Output(param, false)))...) } else { failedList = append(failedList, putils.GetErrMsg2FailedSingleList(v, err, storeID, model.VendorChineseNames[model.VendorIDDD], fmt.Sprintf("上架商品正常,添加固定库存.%s", utils.Format4Output(param, false)))...) } } } if len(failedList) > 0 { err = nil } return failedList, err } // UpdateStoreSkusPrice 更新商品价格 func (p *PurchaseHandler) UpdateStoreSkusPrice(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) { api := getAPI(vendorOrgCode, storeID, vendorStoreID) for _, v := range storeSkuList { //param := &product_editV2_request.ProductEditV2Param{} //param.ProductId = utils.Str2Int64(v.VendorSkuID) //param.Specs = "净重|" + fmt.Sprintf("%f", v.SpecQuality) + v.SpecUnit // //skuSize := make([]*tiktokShop.SpecDetailList, 0, 0) //detail1 := strings.Split(param.Specs, "^") //name1 := strings.Split(strings.Split(detail1[0], "|")[1], ",") //for i := 0; i < len(name1); i++ { // sku := &tiktokShop.SpecDetailList{ // SpecDetailName1: name1[i], // StockNum: v.Stock, // Price: int(v.VendorPrice), // Code: utils.Int2Str(v.SkuID), // StepStockNum: 0, // SupplierID: "", // OuterSkuID: utils.Int2Str(v.NameID), // DeliveryInfos: []*tiktokShop.DeliveryInfos{ // {InfoType: "weight", InfoUnit: v.SpecUnit, InfoValue: fmt.Sprintf("%f", v.SpecQuality)}, // }, // } // skuSize = append(skuSize, sku) //} //data, _ := json.Marshal(skuSize) //param.SpecPrices = string(data) err := api.EditPrice(&sku_editPrice_request.SkuEditPriceParam{ Price: v.VendorPrice, SkuId: utils.Str2Int64(v.VendorSkuAttrId), ProductId: utils.Str2Int64(v.VendorSkuID), }) if err != nil { failedList = append(failedList, putils.GetErrMsg2FailedSingleList(v, err, storeID, model.VendorChineseNames[model.VendorIDDD], "更新价格异常")...) } else { failedList = append(failedList, putils.GetErrMsg2FailedSingleList(v, err, storeID, model.VendorChineseNames[model.VendorIDDD], "更新价格正常")...) } } if len(failedList) > 0 { err = nil } return failedList, err } // 全量/增量更新商品sku库存 func (p *PurchaseHandler) UpdateStoreSkusStock(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) { tiktokApi := getAPI(storeSkuList[0].VendorOrgCode, storeID, vendorStoreID) for _, v := range storeSkuList { err := tiktokApi.UpdateSkuStock(&sku_syncStock_request.SkuSyncStockParam{ SkuId: utils.Str2Int64(v.VendorSkuAttrId), ProductId: utils.Str2Int64(v.VendorSkuID), Incremental: false, IdempotentId: "", StockNum: int64(v.Stock), OutWarehouseId: vendorStoreID, }) if err != nil { failedList = append(failedList, putils.GetErrMsg2FailedSingleList(v, err, storeID, model.VendorChineseNames[model.VendorIDDD], "更新库存错误")...) } else { failedList = append(failedList, putils.GetErrMsg2FailedSingleList(v, err, storeID, model.VendorChineseNames[model.VendorIDDD], "更新库存正常")...) } } return p.UpdateStoreSkusPrice(ctx, vendorOrgCode, storeID, vendorStoreID, storeSkuList) } func (p *PurchaseHandler) GetStoreSkusFullInfo(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (skuNameList []*partner.SkuNameInfo, err error) { // 查询单个门店商品 tiktokApi := getAPI(storeSkuList[0].VendorOrgCode, storeID, vendorStoreID) for _, v := range storeSkuList { skuInfo, err := tiktokApi.GetSkuDetail(v.VendorSkuID, utils.Int2Str(v.SkuID)) if err != nil { return nil, err } if skuName := vendorSku2Jx(skuInfo); skuName != nil { skuNameList = append(skuNameList, skuName) } } //switch len(storeSkuList) { //case 1: // //default: // // 列表查询门店商品 // //var storeSkuMap map[string]*partner.StoreSkuInfo // //if storeSkuList != nil { // // storeSkuMap = putils.StoreSkuList2MapByVendorSkuID(storeSkuList) // //} // //for { // // result, err := mtapi.RetailList(vendorStoreID, len(skuNameList), mtwmapi.GeneralMaxLimit) // // if err != nil { // // return nil, err // // } // // if storeSkuMap == nil { // // skuNameList = append(skuNameList, vendorSkuList2Jx(result)...) // // } else { // // for _, v := range result { // // if storeSkuMap[v.AppFoodCode] != nil { // // if skuName := vendorSku2Jx(v); skuName != nil { // // skuNameList = append(skuNameList, skuName) // // } // // } // // } // // } // // if len(result) < mtwmapi.GeneralMaxLimit { // // break // // } // //} //} return skuNameList, err } func (p *PurchaseHandler) GetSensitiveWordRegexp() *regexp.Regexp { return sensitiveWordRegexp } // CreateStoreSkusAct 创建商品活动 func (p *PurchaseHandler) CreateStoreSkusAct(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) { //actStoreSkuList := putils.StoreSku2ActStoreSku(model.SyncFlagNewMask, vendorStoreID, storeSkuList) //failedList, err = createOneShopAct(putils.GetFixDirectDownAct(vendorOrgCode, storeID, 0), vendorStoreID, actStoreSkuList) //storeSkuMap := putils.StoreSkuList2MapBySkuID(storeSkuList) //for _, v := range actStoreSkuList { // storeSkuMap[v.SkuID].VendorActID = v.VendorActID //} //if len(failedList) > 0 { // err = nil //} return failedList, err } func (p *PurchaseHandler) CancelActs(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) { return nil, err // return cancelOneShopAct(putils.GetFixDirectDownAct(vendorOrgCode, storeID, 0), vendorStoreID, putils.StoreSku2ActStoreSku(model.SyncFlagDeletedMask, vendorStoreID, storeSkuList)) } func (p *PurchaseHandler) UpdateStoreSkusSpecTag(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (err error) { //var foodDataList []map[string]interface{} //for _, v := range storeSkuList { // var foodData = make(map[string]interface{}) // if v.IsSpecialty != 0 && v.IsSpecialty == -1 { // v.IsSpecialty = 0 // } // foodData["is_specialty"] = v.IsSpecialty // foodData["app_food_code"] = v.SkuID // foodDataList = append(foodDataList, foodData) //} //if globals.EnableMtwmStoreWrite { // if len(foodDataList) == 1 { // err = getAPI(vendorOrgCode, storeID, vendorStoreID).RetailInitData(ctx.GetTrackInfo(), vendorStoreID, utils.Int2Str(storeSkuList[0].SkuID), foodDataList[0]) // } else if len(foodDataList) > 0 { // _, err = getAPI(vendorOrgCode, storeID, vendorStoreID).RetailBatchInitData(ctx.GetTrackInfo(), vendorStoreID, foodDataList) // } //} return err }