package jd import ( "fmt" "strings" "git.rosy.net.cn/baseapi/platformapi/jdapi" "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/business/jxutils" "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/globals" "git.rosy.net.cn/jx-callback/globals/api" beego "github.com/astaxie/beego/adapter" ) const ( DefBrandID = 35247 DefJdCategoryID = 20362 DefJdCategoryID4Jxgy = 22410 // 其他国产水果 ) func getDefJdCategoryID() int { if beego.BConfig.RunMode == "jxgy" { return DefJdCategoryID4Jxgy } return DefJdCategoryID } func jdCat2Jx(jdCat *jdapi.CategoryInfo) (jxCat *partner.BareCategoryInfo) { return &partner.BareCategoryInfo{ VendorCatID: utils.Int64ToStr(jdCat.Id), Level: jdCat.Level, Name: jdCat.Name, Seq: jdCat.Sort, } } func (p *PurchaseHandler) GetAllCategories(ctx *jxcontext.Context, vendorOrgCode string) (cats []*partner.BareCategoryInfo, err error) { result, err := getAPI(vendorOrgCode).QueryCategoriesByOrgCode() if err == nil { catMap := make(map[int64]*partner.BareCategoryInfo) level := 1 for { processedCount := 0 for _, jdCat := range result { if jdCat.Level == level { processedCount++ jxCat := jdCat2Jx(jdCat) if level == 1 { cats = append(cats, jxCat) } else { parentCat := catMap[jdCat.ParentId] if parentCat != nil { parentCat.Children = append(parentCat.Children, jxCat) } } catMap[jdCat.Id] = jxCat } } if processedCount == 0 { break } level++ } } return cats, err } func (p *PurchaseHandler) CreateCategory2(ctx *jxcontext.Context, cat *dao.SkuStoreCatInfo) (err error) { globals.SugarLogger.Debugf("CreateCategory2 cat:%s", utils.Format4Output(cat, true)) parentID := int64(0) if cat.Level != 1 { parentID = utils.Str2Int64(cat.ParentVendorCatID) } if globals.EnableJdStoreWrite { result, err2 := getAPI(cat.VendorOrgCode).AddShopCategory(parentID, cat.Name, int(cat.Level), cat.Seq, ctx.GetUserName()) if err = err2; err == nil { if jdID := utils.Str2Int64WithDefault(result, 0); jdID != 0 { cat.VendorCatID = utils.Int64ToStr(jdID) } } } else { cat.VendorCatID = utils.Int64ToStr(jxutils.GenFakeID()) } return err } func (p *PurchaseHandler) UpdateCategory2(ctx *jxcontext.Context, cat *dao.SkuStoreCatInfo) (err error) { globals.SugarLogger.Debugf("UpdateCategory2 cat:%s", utils.Format4Output(cat, true)) if globals.EnableJdStoreWrite { err = getAPI(cat.VendorOrgCode).UpdateShopCategory(utils.Str2Int64(cat.VendorCatID), cat.Name) } return err } func (p *PurchaseHandler) DeleteCategory2(ctx *jxcontext.Context, vendorOrgCode, vendorCatID string) (err error) { globals.SugarLogger.Debugf("DeleteCategory2 vendorOrgCode:%s, vendorCatID:%s", vendorOrgCode, vendorCatID) if globals.EnableJdStoreWrite { err = getAPI(vendorOrgCode).DelShopCategory(utils.Str2Int64(vendorCatID)) } return err } func (p *PurchaseHandler) ReorderCategories2(ctx *jxcontext.Context, vendorOrgCode, vendorParentCatID string, vendorCatIDList []string) (err error) { if globals.EnableJdStoreWrite { err = getAPI(vendorOrgCode).ChangeShopCategoryOrder(utils.Str2Int64WithDefault(vendorParentCatID, 0), utils.StringSlice2Int64(vendorCatIDList)) } return err } func (p *PurchaseHandler) getVendorCategories(level int, pid int64) (vendorCats []*model.SkuVendorCategory, err error) { // 得到平台的分类,不需要指定分账号 cats, err := api.JdAPI.QueryChildCategoriesForOP(pid) if err != nil { return nil, err } for _, v := range cats { if v.Status == 1 { cat := &model.SkuVendorCategory{ VendorID: model.VendorIDJD, Name: v.Name, Level: level, VendorCategoryID: utils.Int64ToStr(v.Id), } if level > 1 { cat.ParentID = utils.Int64ToStr(v.ParentId) if level == 3 { cat.IsLeaf = 1 } } vendorCats = append(vendorCats, cat) if level < 3 { childVendorCats, err2 := p.getVendorCategories(level+1, v.Id) if err2 == nil && len(childVendorCats) > 0 { vendorCats = append(vendorCats, childVendorCats...) } else { cat.IsLeaf = 1 } } } } return vendorCats, nil } func (p *PurchaseHandler) GetVendorCategories(ctx *jxcontext.Context) (vendorCats []*model.SkuVendorCategory, err error) { vendorCats, err = p.getVendorCategories(1, 0) return vendorCats, err } func skuInfo2Param(ctx *jxcontext.Context, sku *dao.StoreSkuSyncInfo) (param *jdapi.OpSkuParam) { param = &jdapi.OpSkuParam{ TraceID: ctx.GetTrackInfo(), OutSkuID: utils.Int2Str(sku.SkuID), ShopCategories: []int64{}, CategoryID: sku.VendorVendorCatID, BrandID: DefBrandID, SkuName: utils.LimitUTF8StringLen(sku.SkuName, jdapi.MaxSkuNameCharCount), SkuPrice: int(sku.Price), Weight: float64(jxutils.IntWeight2Float(sku.Weight)), FixedStatus: jxStatus2jdStatus(sku.MergedStatus), IsSale: jdapi.IsSaleNo, // todo ? Upc: sku.Upc, // Images: jxutils.BatchString2Slice(sku.Img, sku.Img2), } if sku.VendorCatID != "" { param.ShopCategories = append(param.ShopCategories, utils.Str2Int64(sku.VendorCatID)) } if sku.ImgMix != "" { param.Images = jxutils.BatchString2Slice(sku.ImgMix, sku.Img2, sku.Img3, sku.Img4, sku.Img5) } else { param.Images = jxutils.BatchString2Slice(sku.Img, sku.Img2, sku.Img3, sku.Img4, sku.Img5) } if param.CategoryID == 0 { param.CategoryID = int64(getDefJdCategoryID()) } // 京东强制要求upc的商品,如果没有设置upc,自动生成一个假的 if param.Upc == "" && isSkuMustHaveUpc(sku.Unit, param.CategoryID) { param.Upc = jxutils.GenFakeUPC(sku.SkuID) } if sku.IsGlobal == 0 && len(sku.SellCities) > 0 { param.SellCities = utils.StringSlice2Int64(sku.SellCities) } if sku.DescImg != "" { param.ProductDesc = fmt.Sprintf(`一张图片`, sku.DescImg) } return param } func (p *PurchaseHandler) CreateSku2(ctx *jxcontext.Context, sku *dao.StoreSkuSyncInfo) (err error) { globals.SugarLogger.Debugf("CreateSku2 sku:%s", utils.Format4Output(sku, true)) param := skuInfo2Param(ctx, sku) if globals.EnableJdStoreWrite { sku.VendorSkuID, err = getAPI(sku.VendorOrgCode).AddSku2(param) if err != nil && strings.Contains(err.Error(), jdapi.ErrMustUPC) { result, err2 := getAPI(sku.VendorOrgCode).BatchAddSkuByUPC([]*jdapi.CreateByUpcParam2{ &jdapi.CreateByUpcParam2{ OutSkuId: utils.Int2Str(sku.SkuID), Upc: sku.Upc, JdPrice: utils.Int64ToStr(sku.Price), ShopCategoryID: utils.Str2Int64WithDefault((sku.VendorCatID), 0), }, }) if err2 == nil { sku.VendorSkuID = result.SkuID } else { err = err2 } } } else { sku.VendorSkuID = utils.Int64ToStr(jxutils.GenFakeID()) } return err } func (p *PurchaseHandler) UpdateSku2(ctx *jxcontext.Context, sku *dao.StoreSkuSyncInfo) (err error) { globals.SugarLogger.Debugf("UpdateSku2 sku:%s", utils.Format4Output(sku, true)) param := skuInfo2Param(ctx, sku) if globals.EnableJdStoreWrite { _, err = getAPI(sku.VendorOrgCode).UpdateSku2(param) } return err } func (p *PurchaseHandler) DeleteSku2(ctx *jxcontext.Context, vendorOrgCode string, sku *partner.StoreSkuInfo) (err error) { globals.SugarLogger.Debugf("DeleteSku2 vendorOrgCode:%s, sku:%s", vendorOrgCode, utils.Format4Output(sku, true)) // 京东到家只能通过商家ID删除SKU,如果没有的话,先绑定,再删除 if sku.SkuID == 0 { skuPairList := []*jdapi.SkuIDPair{ &jdapi.SkuIDPair{ SkuId: utils.Str2Int64(sku.VendorSkuID), OutSkuId: sku.VendorSkuID, }, } if globals.EnableJdStoreWrite { _, err = getAPI(vendorOrgCode).BatchUpdateOutSkuId(skuPairList) if err != nil { return err } sku.SkuID = int(utils.Str2Int64(sku.VendorSkuID)) } } param := &jdapi.OpSkuParam{ TraceID: ctx.GetTrackInfo(), OutSkuID: utils.Int2Str(sku.SkuID), FixedStatus: jdapi.SkuFixedStatusDeleted, } if globals.EnableJdStoreWrite { _, err = getAPI(vendorOrgCode).UpdateSku2(param) } return err } func (p *PurchaseHandler) GetSkus(ctx *jxcontext.Context, vendorOrgCode string, skuID int, vendorSkuID string) (skuNameList []*partner.SkuNameInfo, err error) { param := &jdapi.QuerySkuParam{ SkuID: utils.Str2Int64WithDefault(vendorSkuID, 0), IsFilterDel: jdapi.IsFilterDelTrue, PageNo: 1, PageSize: jdapi.MaxSkuIDsCount4QueryListBySkuIds, // 为了同时取图,这个值不要大于jdapi.MaxSkuIDsCount4QueryListBySkuIds } for { skuList, _, err2 := getAPI(vendorOrgCode).QuerySkuInfos(param) if err = err2; err != nil { return nil, err } if len(skuList) > 0 { batchSkuNameList := make([]*partner.SkuNameInfo, len(skuList)) for k, v := range skuList { batchSkuNameList[k] = vendorSku2Jx(v) } setSkuNameListPic(vendorOrgCode, batchSkuNameList) skuNameList = append(skuNameList, batchSkuNameList...) } if len(skuList) < param.PageSize { break } param.PageNo++ } return skuNameList, err } func setSkuNameListPic(vendorOrgCode string, skuNameList []*partner.SkuNameInfo) []*partner.SkuNameInfo { jdSkuIDs := make([]int64, len(skuNameList)) for k, v := range skuNameList { jdSkuIDs[k] = utils.Str2Int64(v.SkuList[0].VendorSkuID) } imgMap := make(map[int64]*jdapi.ImgHandleQueryResult) if imgList, err2 := getAPI(vendorOrgCode).QueryListBySkuIds(&jdapi.QueryListBySkuIdsParam{ SkuIDs: jdSkuIDs, }); err2 == nil { for _, v := range imgList { if v.ImgType == jdapi.ImgTypeMain { imgResult := imgMap[v.SkuID] if imgResult == nil || imgResult.IsMain < v.IsMain { imgMap[v.SkuID] = v } } } } // 使用扒页面方式获取商品图片 if false { var leftJdSkuIDs []int64 for _, v := range jdSkuIDs { if imgMap[v] == nil { leftJdSkuIDs = append(leftJdSkuIDs, v) } } task := tasksch.NewParallelTask("jd setSkuNameListPic", nil, jxcontext.AdminCtx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { jdSkuID := batchItemList[0].(int64) imgList, err := getAPI(vendorOrgCode).GetSkuPageImageInfo(jdSkuID) if err == nil && len(imgList) > 0 { retVal = [][]string{ []string{utils.Int64ToStr(jdSkuID), imgList[0].Big}, } } return retVal, err }, leftJdSkuIDs) task.Run() if resultList, err := task.GetResult(0); err == nil { for _, v := range resultList { strList := v.([]string) imgMap[utils.Str2Int64(strList[0])] = &jdapi.ImgHandleQueryResult{ SourceImgURL: strList[1], } } } } // 设置商品图片 for _, v := range skuNameList { if imgResult := imgMap[utils.Str2Int64(v.SkuList[0].VendorSkuID)]; imgResult != nil { v.PictureList = []string{imgResult.SourceImgURL} } } return skuNameList } func vendorSku2Jx(vendorSku *jdapi.SkuMain) (skuName *partner.SkuNameInfo) { prefix, name, comment, specUnit, unit, specQuality := jxutils.SplitSkuName(vendorSku.SkuName) weight := int(vendorSku.Weight * 1000) if weight <= 0 { weight = jxutils.FormatSkuWeight(specQuality, specUnit) } skuID := int(utils.Str2Int64WithDefault(vendorSku.OutSkuID, 0)) vendorSkuID := utils.Int64ToStr(vendorSku.SkuID) skuName = &partner.SkuNameInfo{ NameID: skuID, VendorNameID: vendorSkuID, VendorCatIDList: []string{utils.Int64ToStr(vendorSku.CategoryID)}, Prefix: prefix, Name: name, Unit: unit, SkuList: []*partner.SkuInfo{ &partner.SkuInfo{ StoreSkuInfo: partner.StoreSkuInfo{ VendorSkuID: vendorSkuID, SkuID: skuID, VendorPrice: int64(vendorSku.SkuPrice), Status: jdStatus2jxStatus(vendorSku.FixedStatus), }, SkuName: vendorSku.SkuName, Comment: comment, SpecQuality: float64(specQuality), SpecUnit: specUnit, Weight: weight, }, }, } return skuName } func jdStatus2jxStatus(jdStatus int) (jxStatus int) { switch jdStatus { case jdapi.SkuFixedStatusOnline: jxStatus = model.SkuStatusNormal case jdapi.SkuFixedStatusOffline: jxStatus = model.SkuStatusDontSale case jdapi.SkuFixedStatusDeleted: jxStatus = model.SkuStatusDeleted } return jxStatus } func jxStatus2jdStatus(jxStatus int) (jdStatus int) { switch jxStatus { case model.SkuStatusNormal: jdStatus = jdapi.SkuFixedStatusOnline case model.SkuStatusDontSale: jdStatus = jdapi.SkuFixedStatusOffline case model.SkuStatusDeleted: jdStatus = jdapi.SkuFixedStatusDeleted } return jdStatus } func isSkuMustHaveUpc(unit string, vendorVendorCatID int64) bool { return unit != model.SpecialUnit || !upcLessMap[vendorVendorCatID] } var ( upcLessMap = map[int64]bool{ 20250: true, 20252: true, 20258: true, 20259: true, 20261: true, 20262: true, 20263: true, 20264: true, 20265: true, 20266: true, 20267: true, 22822: true, 20269: true, 20270: true, 20271: true, 20272: true, 20273: true, 20275: true, 20276: true, 20277: true, 20278: true, 20279: true, 20281: true, 20282: true, 20283: true, 20285: true, 20286: true, 20287: true, 22821: true, 20289: true, 20290: true, 23018: true, 20354: true, 20355: true, 20357: true, 20359: true, 23019: true, 20294: true, 20295: true, 20296: true, 20297: true, 20298: true, 20299: true, 20300: true, 20302: true, 20303: true, 20304: true, 22840: true, 22841: true, 20317: true, 20320: true, 20321: true, 20323: true, 20325: true, 20326: true, 20328: true, 20329: true, 20331: true, 20335: true, 20337: true, 20338: true, 20339: true, 22842: true, 22843: true, 23020: true, 20309: true, 20310: true, 20311: true, 20312: true, 20313: true, 20314: true, 20315: true, 22410: true, 23050: true, 20319: true, 20322: true, 20330: true, 20332: true, 20334: true, 20336: true, 20340: true, 20342: true, 23049: true, 20356: true, 20358: true, 20360: true, 20361: true, 20362: true, 20364: true, } )