package tiktok_api import ( "encoding/json" "errors" brand_list_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/brand_list/request" order_batchSensitive_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/order_batchSensitive/request" product_GetRecommendCategory_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_GetRecommendCategory/request" product_addV2_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_addV2/request" product_addV2_response "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_addV2/response" product_auditList_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_auditList/request" product_auditList_response "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_auditList/response" product_del_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_del/request" product_detail_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_detail/request" product_detail_response "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_detail/response" product_detail_request_out "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_detail_out_productId/request" product_editV2_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_editV2/request" product_editV2_commit_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_editV2_commit/request" product_getCatePropertyV2_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_getCatePropertyV2/request" product_getCatePropertyV2_response "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_getCatePropertyV2/response" product_getProductUpdateRule_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_getProductUpdateRule/request" product_listV2_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_listV2/request" product_listV2_response "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_listV2/response" product_qualificationConfig_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_qualificationConfig/request" product_qualificationConfig_response "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_qualificationConfig/response" product_setOffline_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/product_setOffline/request" shop_getShopCategory_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/shop_getShopCategory/request" 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" sku_syncStockBatch_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/sku_syncStockBatch/request" superm_product_batchApplyStoreProductPrice_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/superm_product_batchApplyStoreProductPrice/request" superm_product_batchRedistributeStoreProduct_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/superm_product_batchRedistributeStoreProduct/request" superm_product_createSubProduct_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/superm_product_createSubProduct/request" superm_product_launchProduct_request "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/superm_product_launchProduct/request" "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/globals" "io/ioutil" "net/http" "strings" ) // UpdateSkuStock // 1、支持修改普通库存,区域库存,阶梯库存 // 2、支持增量更新,当incremental=true时idempotent_id字段参数必传。例:原商品库存是10个,接口传入5个,执行成功商品库存是15个。 // 3、支持全量更新。例:原商品库存是10个,接口传入5个,执行成功商品库存是5个。 // 4、可以设置库存为0 // 5、超市小时达店铺类型,更新库存out_warehouse_id= store_id(门店id)表示更新门店商品库存。 func (a *API) UpdateSkuStock(param *sku_syncStock_request.SkuSyncStockParam) error { request := sku_syncStock_request.New() request.Param = param resp, err := request.Execute(a.accessTokenObj) if err != nil { return err } if resp.Code != RequestSuccessCode { return errors.New(resp.SubMsg + ":" + resp.LogId) } return nil } // GetShopCategory 获取商品类目 // 请一定使用最小层级类目id,发布商品或更新商品。 // 接口使用注意点: // 1、第一次请求cid=0获取所有一级类目 // 2、先判断enable=true,如果enable=false表示该类目已经失效,请勿使用。 // 3、循环获取最小层级类目id方式:再判断is_leaf=true或false。is_leaf=true表示是叶子节点,最小层级类目id。is_leaf=false表示不是子节点,请求参数cid=上一次响应参数id,直到获取最小层级类目id。 func (a *API) GetShopCategory(cid int64) ([]*RetailCategoryInfo, error) { // 如果cid为0,则获取所有的分类,如果cid不为0,则获取该分类下面的所有子分类 request := shop_getShopCategory_request.New() param := request.GetParams() param.Cid = cid result, err := request.Execute(a.accessTokenObj) if err != nil { return nil, err } if result.Code != RequestSuccessCode { return nil, errors.New(result.SubMsg + ":" + result.LogId) } categoryAll := make([]*RetailCategoryInfo, 0, 0) for i := 0; i < len(result.Data); i++ { if result.Data[i].Enable != true { // 失效了 continue } if result.Data[i].IsLeaf == false { categoryAll = append(categoryAll, &RetailCategoryInfo{ DataItem: result.Data[i], Children: make([]*RetailCategoryInfo, 0, 0), }) parentNode, _ := a.GetShopCategory(result.Data[i].Id) categoryAll[i].Children = append(categoryAll[i].Children, parentNode...) } else { node := RetailCategoryInfo{ DataItem: result.Data[i], Children: nil, } categoryAll = append(categoryAll, &node) } } return categoryAll, nil } // GetCatePropertyV2 根据商品分类获取对应的属性列表 // 参数为最终的子节点id下面再无子节点 func (a *API) GetCatePropertyV2(categoryLeftId int64) (*product_getCatePropertyV2_response.ProductGetCatePropertyV2Response, error) { request := product_getCatePropertyV2_request.New() param := request.GetParams() param.CategoryLeafId = categoryLeftId result, err := request.Execute(a.accessTokenObj) if err != nil { return nil, err } if result.Code != RequestSuccessCode { return nil, errors.New(result.SubMsg + ":" + result.LogId) } return result, nil } // CreateStoreCommodity (新增总部商品) func (a *API) CreateStoreCommodity(skuParam *product_addV2_request.ProductAddV2Param) (*product_addV2_response.ProductAddV2Data, error) { qualityList := make([]product_addV2_request.QualityListItem, 0, 0) switch a.accessTokenObj.ShopId { case TiktokFloderKeyVegetableKey: // 京西速食 qualityList = append(qualityList, product_addV2_request.QualityListItem{ QualityKey: QualificationsSuShiPackageId, QualityName: QualificationsSuShiPackageName, QualityAttachments: []product_addV2_request.QualityAttachmentsItem{ product_addV2_request.QualityAttachmentsItem{ MediaType: 1, Url: QualificationsPackageImg, }, }, }) case TiktokFloderKeyShoppingKey: // 京西超市 qualityList = append(qualityList, product_addV2_request.QualityListItem{ QualityKey: QualificationsShopPackageId, QualityName: QualificationsShopPackageName, QualityAttachments: []product_addV2_request.QualityAttachmentsItem{ product_addV2_request.QualityAttachmentsItem{ MediaType: 1, Url: QualificationsShopPackageImg, }, }, }) case TiktokFloderKeyBeautifulKey: // 美好菜市 qualityList = append(qualityList, product_addV2_request.QualityListItem{ QualityKey: QualificationsBeautifulPackageId, QualityName: QualificationsBeautifulPackageName, QualityAttachments: []product_addV2_request.QualityAttachmentsItem{ product_addV2_request.QualityAttachmentsItem{ MediaType: 1, Url: QualificationsBeautifulPackageImg, }, }, }) default: return nil, errors.New("创建商品是,资质获取异常") } skuParam.QualityList = qualityList request := product_addV2_request.New() request.Param = skuParam result, err := request.Execute(a.accessTokenObj) if err != nil { return nil, err } if result.Code != RequestSuccessCode { return nil, errors.New(result.SubMsg + ":" + result.LogId) } return result.Data, nil } func (a *API) QualityList(categoryId int64) ([]*product_qualificationConfig_response.ConfigListItem, error) { if categoryId <= 0 { return nil, errors.New("categoryId 不能为0") } request := product_qualificationConfig_request.New() request.Param.CategoryId = categoryId result, err := request.Execute(a.accessTokenObj) if err != nil { return nil, err } if result.Code != RequestSuccessCode { return nil, errors.New(result.SubMsg + ":" + result.LogId) } configListRequired := make([]*product_qualificationConfig_response.ConfigListItem, 0) for _, v := range result.Data.ConfigList { if v.IsRequired == false { continue } configListRequired = append(configListRequired, &v) } globals.SugarLogger.Debugf("configListRequired=== :%s", utils.Format4Output(configListRequired, false)) return configListRequired, nil } // SyncStockBatch 库存批量同步 func (a *API) SyncStockBatch(param *sku_syncStockBatch_request.SkuSyncStockBatchParam) error { request := sku_syncStockBatch_request.New() request.Param = param result, err := request.Execute(a.accessTokenObj) if err != nil { return err } if result.Code != RequestSuccessCode { return errors.New(result.SubMsg + ":" + result.LogId) } return nil } // EditStoreCommodity 编辑商品 func (a *API) EditStoreCommodity(sku *product_editV2_request.ProductEditV2Param) error { request := product_editV2_request.New() request.Param = sku result, err := request.Execute(a.accessTokenObj) if err != nil { return err } if result.Code != RequestSuccessCode { return errors.New(result.SubMsg + ":" + result.LogId) } return nil } // EditStoreCommodityCommit 上架商品 func (a *API) EditStoreCommodityCommit(sku *product_editV2_commit_request.ProductEditV2Param2) error { request := product_editV2_commit_request.New() request.Param = sku result, err := request.Execute(a.accessTokenObj) if err != nil { return err } if result.Code != RequestSuccessCode { return errors.New(result.SubMsg + ":" + result.LogId) } return nil } // DeleteStoreCommodity 删除商品 func (a *API) DeleteStoreCommodity(productId int64) error { request := product_del_request.New() param := request.GetParams() param.ProductId = productId param.DeleteForever = true result, err := request.Execute(a.accessTokenObj) if err != nil { return err } if result.Code != RequestSuccessCode { return errors.New(result.SubMsg + ":" + result.LogId) } return nil } // ProductSetOffline 商品上下架 func (a *API) ProductSetOffline(productId int64) error { request := product_setOffline_request.New() param := request.GetParams() param.ProductId = productId result, err := request.Execute(a.accessTokenObj) if err != nil { return err } if result.Code != RequestSuccessCode { return errors.New(result.SubMsg + ":" + result.LogId) } return nil } // GetSkuDetail 查询单个商品详情(这个只有抖店平台商品id有效) func (a *API) GetSkuDetail(productId, outProductId string) (*product_detail_response.ProductDetailData, error) { request := product_detail_request.New() param := request.GetParams() if productId != "" { param.ProductId = productId } result, err := request.Execute(a.accessTokenObj) if err != nil { return nil, err } if strings.Contains(result.SubMsg, "未找到或商品已删除") { return nil, nil } if result.Code != RequestSuccessCode { return nil, errors.New(result.SubMsg + ":" + result.LogId) } if result.Data.Status == 2 { return nil, nil } return result.Data, nil } // GetSkuDetailLocalID 查询单个商品详情(这个只有京西平台商品id有效,查询到的商品可能为子商品也可能为主商品,主商品删除时,子商品和主商品状态不一致) func (a *API) GetSkuDetailLocalID(productId, outProductId string) (*product_detail_response.ProductDetailData, error) { request := product_detail_request_out.New() param := request.GetParams() if outProductId != "" { param.OutProductId = outProductId } result, err := request.Execute(a.accessTokenObj) if strings.Contains(result.SubMsg, "未找到或商品已删除") { return nil, nil } if err != nil { return nil, err } if result.Code != RequestSuccessCode { return nil, errors.New(result.SubMsg + ":" + result.LogId) } if result.Data.Status == 2 { return nil, nil } return result.Data, nil } // GetSkuDetailList 批量获取商品列表 func (a *API) GetSkuDetailList(param *product_listV2_request.ProductListV2Param) (*product_listV2_response.ProductListV2Data, error) { request := product_listV2_request.New() request.Param = param result, err := request.Execute(a.accessTokenObj) if err != nil { return nil, err } if result.Code != RequestSuccessCode { return nil, errors.New(result.SubMsg + ":" + result.LogId) } return result.Data, nil } // EditPrice 编辑商品价格 func (a *API) EditPrice(skuPrice *sku_editPrice_request.SkuEditPriceParam) error { request := sku_editPrice_request.New() request.Param = skuPrice result, err := request.Execute(a.accessTokenObj) if err != nil { return err } if result.Code != RequestSuccessCode { return errors.New(result.SubMsg + ":" + result.LogId) } return nil } // GetSkuBrand 获取商品所属品牌 func (a *API) GetSkuBrand(categoryId int64, brandName string) (int64, error) { request := brand_list_request.New() request.Param.CategoryId = categoryId request.Param.Query = brandName result, err := request.Execute(a.accessTokenObj) if err != nil { return 0, err } if result.Code != RequestSuccessCode { return 0, errors.New(result.SubMsg) } if result.Data == nil || len(result.Data.BrandList) == 0 { return 596120136, nil // 无品牌默认值 } var brandNameLen int = 0 var BrandNameMap = make(map[int]int64, 0) for _, v := range result.Data.BrandList { BrandNameMap[len(v.NameCn)] = v.BrandId if brandNameLen == 0 { brandNameLen = len(v.NameCn) continue } if len(v.NameCn) < brandNameLen && brandNameLen != 0 { brandNameLen = len(v.NameCn) } } //for _, vv := range result.Data.BrandList { // if vv.BrandId == BrandNameMap[brandNameLen] { // globals.SugarLogger.Debugf("brand name :[%s]", vv.NameCn) // } //} return BrandNameMap[brandNameLen], nil } type YunDecrypt struct { Code string `json:"code"` Data string `json:"data"` Err string `json:"err"` } // OrderUserInfoDecrypt 用户信息解密(此接口转入云鼎服务器,将不再直接通信) func (a *API) OrderUserInfoDecrypt(orderId, name, tel, address string) (string, string, string, error) { url := "http://ddy.jxc4.com/tt/decrypt" //url := "http://180.184.32.54:8080/tt/decrypt" accessToken, err := json.Marshal(a.accessTokenObj.CreateTokenData) if err != nil { return "", "", "", err } param := make(map[string]string, 5) param["token"] = string(accessToken) param["encrypt_post_tel"] = tel param["encrypt_post_receiver"] = address param["encrypt_post_name"] = name param["order_id"] = orderId param["app_key"] = a.appKey param["app_secret"] = a.appSecret data, err := json.Marshal(param) if err != nil { return "", "", "", nil } payload := strings.NewReader(string(data)) req, _ := http.NewRequest("POST", url, payload) req.Header.Add("content-type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { return "", "", "", err } defer res.Body.Close() body, _ := ioutil.ReadAll(res.Body) var result *YunDecrypt if err := json.Unmarshal(body, &result); err != nil { return "", "", "", err } if result.Code != "200" { return "", "", "", errors.New(result.Err) } if len(result.Data) != 0 { var decryData map[string]string if err := json.Unmarshal([]byte(result.Data), &decryData); err != nil { return "", "", "", err } return decryData["name"], decryData["tel"], decryData["address"], nil } return "", "", "", errors.New("返回值为空") //request := order_batchDecrypt_request.New() // //cipherInfos := make([]order_batchDecrypt_request.CipherInfosItem, 0, 0) //for i := 1; i <= 3; i++ { // cipher := order_batchDecrypt_request.CipherInfosItem{} // cipher.AuthId = orderId // switch i { // case 1: // 姓名 // cipher.CipherText = name // cipherInfos = append(cipherInfos, cipher) // case 2: // 手机号 // cipher.CipherText = tel // cipherInfos = append(cipherInfos, cipher) // case 3: // 地址 // cipher.CipherText = address // cipherInfos = append(cipherInfos, cipher) // } //} //request.Param.CipherInfos = cipherInfos // //result, err := request.Execute(a.accessTokenObj) //globals.SugarLogger.Debugf("OrderUserInfoDecrypt======:%s", utils.Format4Output(result, false)) //if err != nil { // return "", "", "", err //} //if result.Code != RequestSuccessCode { // return "", "", "", errors.New(result.SubCode) //} // //var ( // userName, userTel, userAddress = "", "", "" //) //for _, v := range result.Data.DecryptInfos { // if v.CipherText != "" && v.CipherText == name { // userName = v.DecryptText // } // if v.CipherText != "" && v.CipherText == tel { // userTel = v.DecryptText // } // if v.CipherText != "" && v.CipherText == address { // userAddress = v.DecryptText // } //} //return userName, userTel, userAddress, err } // OrderUserInfoSensitive 用户信息脱敏 func (a *API) OrderUserInfoSensitive(orderId, name, tel, address string) { request := order_batchSensitive_request.New() paramItems := make([]order_batchSensitive_request.CipherInfosItem, 0, 0) for i := 0; i < 3; i++ { cipher := order_batchSensitive_request.CipherInfosItem{} cipher.AuthId = orderId switch i { case 1: // 姓名 cipher.CipherText = name paramItems = append(paramItems, cipher) case 2: // 手机号 cipher.CipherText = tel paramItems = append(paramItems, cipher) case 3: // 地址 cipher.CipherText = address paramItems = append(paramItems, cipher) } } request.Param.CipherInfos = paramItems request.Execute(a.accessTokenObj) } // GetProductUpdateRule 查询分类是否支持七天无理由退货 func (a *API) GetProductUpdateRule(categoryId int64) bool { request := product_getProductUpdateRule_request.New() param := request.GetParams() param.CategoryId = categoryId result, err := request.Execute(a.accessTokenObj) if err != nil { return false } if result.Code != RequestSuccessCode { return false } return result.Data.AfterSaleRule.SupplyDayReturnRule.Enable } // GetProductUpdateRule2 查询分类是否支持七天无理由退货 func (a *API) GetProductUpdateRule2(categoryId int64) bool { request := product_getProductUpdateRule_request.New() param := request.GetParams() param.CategoryId = categoryId result, err := request.Execute(a.accessTokenObj) if err != nil { return false } if result.Code != RequestSuccessCode { return false } return result.Data.AfterSaleRule.SupplyDayReturnRule.Enable } // GetProductAuditList 获取商品的审核记录 func (a *API) GetProductAuditList(page, pageSize, status int64) ([]product_auditList_response.RecordsItem, int64, error) { request := product_auditList_request.New() request.Param.PublishStatus = status request.Param.Page = page request.Param.Size = pageSize result, err := request.Execute(a.accessTokenObj) if err != nil { return nil, 0, err } if result.Code != RequestSuccessCode { return nil, 0, errors.New(result.SubMsg + ":" + result.LogId) } return result.Data.Records, result.Data.Total, nil } // GetRecommendCategory 根据图片预测商品类目,优先四级>三级>二级>1级 func (a *API) GetRecommendCategory(picParams []string) (int64, error) { if len(picParams) == 0 { return 0, errors.New("抖音根据图片预测分类,图片不能为空") } request := product_GetRecommendCategory_request.New() request.Param.Scene = "smart_publish" // 根据图片获取分类属性 picList := make([]product_GetRecommendCategory_request.PicItem, 0, 0) for _, v := range picParams { picList = append(picList, product_GetRecommendCategory_request.PicItem{Url: v}) } request.Param.Pic = picList result, err := request.Execute(a.accessTokenObj) if err != nil { return 0, err } if result.Code != RequestSuccessCode { return 0, errors.New(result.SubMsg + ":" + result.LogId) } if len(result.Data.CategoryDetails) == 0 { return 0, errors.New("抖音根据图片获取分类,返回值为0") } var firstCid int64 var secondCid int64 var thirdCid int64 var fourthCid int64 if result.Data.CategoryDetails[0].CategoryDetail.FirstCid != 0 { firstCid = result.Data.CategoryDetails[0].CategoryDetail.FirstCid } if result.Data.CategoryDetails[0].CategoryDetail.SecondCid != 0 { secondCid = result.Data.CategoryDetails[0].CategoryDetail.SecondCid } if result.Data.CategoryDetails[0].CategoryDetail.ThirdCid != 0 { thirdCid = result.Data.CategoryDetails[0].CategoryDetail.ThirdCid } if result.Data.CategoryDetails[0].CategoryDetail.FourthCid != 0 { fourthCid = result.Data.CategoryDetails[0].CategoryDetail.FourthCid } if fourthCid != 0 { return fourthCid, nil } if thirdCid != 0 { return thirdCid, nil } if secondCid != 0 { return secondCid, nil } if firstCid != 0 { return firstCid, nil } return 0, errors.New("抖音获取推荐分类异常") } /*--------------------------------方案二 将住上铺平铺到所有门店店中,不在使用子商品------------------------------------------------------------------------------------*/ // CreateSubProduct 基于店铺主品创建门店子品 (同步创建) func (a *API) CreateSubProduct(mainProductId int64, storeId int64) (int64, error) { request := superm_product_createSubProduct_request.New() request.Param.MainProductId = mainProductId request.Param.StoreId = storeId result, err := request.Execute(a.accessTokenObj) if err != nil { return 0, err } if result.Code != RequestSuccessCode { return 0, errors.New(result.SubMsg + ":" + result.LogId) } return result.Data.SubProductId, nil } // BatchRedistributeStoreProduct 重新分配店铺主品对应的门店子品 (异步执行) func (a *API) BatchRedistributeStoreProduct(param *superm_product_batchRedistributeStoreProduct_request.TaskParams) (int64, error) { request := superm_product_batchRedistributeStoreProduct_request.New() request.Param.TaskParams = param result, err := request.Execute(a.accessTokenObj) if err != nil { return 0, err } if result.Code != RequestSuccessCode { return 0, errors.New(result.SubMsg + ":" + result.LogId) } return result.Data.RootTaskId, nil } // BatchApplyStoreProductPrice 同步主商品价格到子商品 func (a *API) BatchApplyStoreProductPrice(mainProductId int64) (int64, error) { request := superm_product_batchApplyStoreProductPrice_request.New() request.Param.TaskParams.MainProductId = mainProductId result, err := request.Execute(a.accessTokenObj) if err != nil { return 0, err } if result.Code != RequestSuccessCode { return 0, errors.New(result.SubMsg + ":" + result.LogId) } return result.Data.RootTaskId, nil } // LaunchProduct 上架商品 ,支持主商品和子商品上架 func (a *API) LaunchProduct(productId int64) error { request := superm_product_launchProduct_request.New() request.Param.ProductId = productId result, err := request.Execute(a.accessTokenObj) if err != nil { return err } if result.Code != RequestSuccessCode { return errors.New(result.SubMsg + ":" + result.LogId) } return nil }