package cms import ( "errors" "fmt" "strconv" "strings" "time" "git.rosy.net.cn/jx-callback/business/jxutils/tasksch" "git.rosy.net.cn/baseapi/platformapi/jdapi" "git.rosy.net.cn/jx-callback/globals/api" "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils/errlist" "git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/jxutils/datares" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "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" ) type SkuNamesInfo struct { TotalCount int `json:"totalCount"` SkuNames []*model.SkuNameExt `json:"skuNames"` } var ( ErrInputCatsDoesntMatch = errors.New("输入的类别列表不合法,需要输入一个父ID下的所有子类别") ) var ( ebaiUploadRTFShopID string // 饿百找一个店用于调用SkuUploadRTF ) // func getAndSetEbaiUploadRTFShopID() (shopID string) { // if ebaiUploadRTFShopID == "" { // if storeDetail, err := dao.GetStoreDetail(dao.GetDB(), 0, model.VendorIDEBAI); err == nil { // ebaiUploadRTFShopID = utils.Int2Str(storeDetail.Store.ID) // } // } // return ebaiUploadRTFShopID // } // parentID 为-1表示所有 func GetVendorCategories(ctx *jxcontext.Context, vendorID int, parentID string) (vendorCats []*model.SkuVendorCategory, err error) { cond := map[string]interface{}{ model.FieldVendorID: vendorID, } if parentID != "-1" { cond[model.FieldParentID] = parentID } return vendorCats, dao.GetEntitiesByKV(nil, &vendorCats, cond, false) } // parentID 为-1表示所有 func GetCategories(ctx *jxcontext.Context, parentID int) (catList []*dao.SkuCategoryWithVendor, err error) { db := dao.GetDB() cats, err := dao.GetCategories(db, parentID, 0, nil) if err == nil { var ids []int for _, v := range cats { ids = append(ids, v.ID) } thingMapMap, err2 := dao.GetThingMapMap(db, model.ThingTypeCategory, nil, ids) // globals.SugarLogger.Debug(utils.Format4Output(thingMapMap, false)) if err = err2; err == nil { for _, v := range cats { catList = append(catList, &dao.SkuCategoryWithVendor{ SkuCategory: v, MapList: thingMapMap[int64(v.ID)], }) } } } return catList, err } func AddCategory(ctx *jxcontext.Context, cat *model.SkuCategory, userName string) (outCat *model.SkuCategory, err error) { db := dao.GetDB() if cat.Level < 0 || cat.Level > 2 { return nil, errors.New("Level必须为1或2") } else if cat.Level == 1 && cat.ParentID != 0 { return nil, errors.New("Level1的分类其父分类必须为0") } else if cat.Level == 2 { if cat.ParentID == 0 { return nil, errors.New("Level2的分类其父分类必须不为0") } parentCat := &model.SkuCategory{} parentCat.ID = cat.ParentID if err = dao.GetEntity(db, parentCat); err != nil { return nil, err } if parentCat.Level != 1 { return nil, errors.New("Level2的分类其父分类必须为Level1分类") } } dao.WrapAddIDCULDEntity(cat, userName) cat.JdSyncStatus = model.SyncFlagNewMask cat.JdID = 0 cat.Status = model.CategoryStatusEnable cat.Name = strings.Trim(cat.Name, " ") if cat.Img != "" { _, err2 := datares.TryRegisterDataResource(ctx, cat.Name, cat.Img, model.ImgTypeLocal, false) if err = err2; err != nil { return nil, err } } if cat.Seq <= 0 { var maxSeq struct { MaxSeq int } if err = dao.GetRow(db, &maxSeq, "SELECT MAX(seq) max_seq FROM sku_category t1 WHERE level = ?", cat.Level); err != nil { return nil, err } cat.Seq = maxSeq.MaxSeq + 1 } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if err = dao.CreateEntity(db, cat); err != nil { dao.Rollback(db) return nil, err } if err = OnCreateThing(ctx, db, nil, int64(cat.ID), model.ThingTypeCategory); err != nil { dao.Rollback(db) return nil, err } dao.Commit(db) outCat = cat _, err = CurVendorSync.SyncCategory(ctx, nil, cat.ID, false, userName) return outCat, err } func UpdateCategory(ctx *jxcontext.Context, categoryID int, payload map[string]interface{}, userName string) (num int64, err error) { cat := &model.SkuCategory{} cat.ID = categoryID db := dao.GetDB() if err = dao.GetEntity(db, cat); err != nil { return 0, err } valid := dao.StrictMakeMapByStructObject(payload, cat, userName) if len(valid) > 0 { syncStatus := 0 if valid["name"] != nil { valid["name"] = strings.Trim(valid["name"].(string), " ") syncStatus = model.SyncFlagModifiedMask valid[model.FieldJdSyncStatus] = int8(syncStatus) | cat.JdSyncStatus } if valid["status"] != nil { if utils.Interface2Int64WithDefault(valid["status"], -1) == model.CategoryStatusDisabled { if skuList, err2 := dao.GetSkuByCats(db, []int{categoryID}); err2 == nil && len(skuList) > 0 { return 0, fmt.Errorf("暂不允许禁用分类下有商品的分类!") } } } if valid["img"] != nil { if imgStr := utils.Interface2String(valid["img"]); imgStr != "" { _, err2 := datares.TryRegisterDataResource(ctx, cat.Name, utils.Interface2String(valid["img"]), model.ImgTypeLocal, false) if err = err2; err != nil { return 0, err } } } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if num, err = dao.UpdateEntityLogically(db, cat, valid, userName, nil); err != nil { dao.Rollback(db) return 0, err } if err = OnUpdateThing(ctx, db, nil, int64(categoryID), model.ThingTypeCategory); err != nil { dao.Rollback(db) return 0, err } dao.Commit(db) SetStoreCategorySyncStatus2(db, nil, []int{categoryID}, model.SyncFlagModifiedMask) var skuIDs []int if valid["jdCategoryID"] != nil || valid["ebaiCategoryID"] != nil || valid["mtwmCategoryID"] != nil || valid["jdPricePercentage"] != nil || valid["ebaiPricePercentage"] != nil || valid["mtwmPricePercentage"] != nil { if skuList, err2 := dao.GetSkuByCats(db, []int{categoryID}); err2 == nil && len(skuList) > 0 { for _, sku := range skuList { skuIDs = append(skuIDs, sku.ID) } if valid["jdCategoryID"] != nil { dao.SetSkuSyncStatus(db, model.VendorIDJD, skuIDs, model.SyncFlagModifiedMask) } // todo 如下逻辑,在不同平台同时改pricePercentage与平台分类映射时,会不必要的打上多余的标记 var vendorIDs []int syncStatus := model.SyncFlagModifiedMask if valid["jdPricePercentage"] != nil { vendorIDs = append(vendorIDs, model.VendorIDJD) syncStatus |= model.SyncFlagPriceMask } if valid["ebaiPricePercentage"] != nil { vendorIDs = append(vendorIDs, model.VendorIDEBAI) syncStatus |= model.SyncFlagPriceMask } else if valid["ebaiCategoryID"] != nil { vendorIDs = append(vendorIDs, model.VendorIDEBAI) } if valid["mtwmPricePercentage"] != nil { vendorIDs = append(vendorIDs, model.VendorIDMTWM) syncStatus |= model.SyncFlagPriceMask } else if valid["mtwmCategoryID"] != nil { vendorIDs = append(vendorIDs, model.VendorIDMTWM) } if len(vendorIDs) > 0 { SetStoreSkuSyncStatus2(db, nil, vendorIDs, skuIDs, syncStatus) } } } _, err = CurVendorSync.SyncCategory(ctx, db, categoryID, false, userName) if len(skuIDs) > 0 { CurVendorSync.SyncSkus(ctx, db, nil, skuIDs, true, true, userName) } } return num, err } func SetStoreCategorySyncStatus2(db *dao.DaoDB, storeIDs []int, catIDs []int, syncStatus int) (num int64, err error) { for _, vendorID := range partner.GetSingleStoreVendorIDs() { num2, err2 := dao.SetStoreCategorySyncStatus(db, vendorID, storeIDs, catIDs, syncStatus) if err = err2; err != nil { return 0, err } num += num2 } return num, nil } func ReorderCategories(ctx *jxcontext.Context, parentID int, categoryIDs []int, userName string) (err error) { var cats []*model.SkuCategory db := dao.GetDB() if err = dao.GetEntitiesByKV(db, &cats, utils.Params2Map(model.FieldParentID, parentID), false); err == nil { catsLen := len(cats) if catsLen != len(categoryIDs) { return ErrInputCatsDoesntMatch } catsMap := make(map[int]*model.SkuCategory, catsLen) for _, cat := range cats { catsMap[cat.ID] = cat } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() for k, v := range categoryIDs { if catsMap[v] == nil { dao.Rollback(db) return fmt.Errorf("分类:%d不在%d分类下", v, parentID) } catsMap[v].Seq = k catsMap[v].LastOperator = ctx.GetUserName() if _, err = dao.UpdateEntity(db, catsMap[v]); err != nil { dao.Rollback(db) return err } if err = OnUpdateThing(ctx, db, nil, int64(catsMap[v].ID), model.ThingTypeCategory); err != nil { dao.Rollback(db) return err } } dao.Commit(db) SetStoreCategorySyncStatus2(db, nil, categoryIDs, model.SyncFlagModifiedMask) if err == nil { _, err = CurVendorSync.SyncReorderCategories(ctx, db, parentID, false, userName) CurVendorSync.SyncStoresCategory(ctx, db, nil, nil, false, true, true) } } return err } func DeleteCategory(ctx *jxcontext.Context, categoryID int, userName string) (num int64, err error) { cat := &model.SkuCategory{} cat.ID = categoryID var countInfos []*struct{ Ct int } db := dao.GetDB() if err = dao.GetRows(db, &countInfos, ` SELECT COUNT(*) ct FROM sku t1 WHERE t1.category_id = ? AND t1.deleted_at = ? UNION ALL SELECT COUNT(*) ct FROM sku_name t1 WHERE t1.category_id = ? AND t1.deleted_at = ? UNION ALL SELECT COUNT(*) ct FROM sku_category t1 WHERE t1.parent_id = ? AND t1.deleted_at = ? `, categoryID, utils.DefaultTimeValue, categoryID, utils.DefaultTimeValue, categoryID, utils.DefaultTimeValue, &countInfos); err == nil { if countInfos[0].Ct != 0 { return 0, errors.New("还有商品使用此类别,不能删除") } else if countInfos[1].Ct != 0 { return 0, errors.New("还有商品名使用此类别,不能删除") } else if countInfos[2].Ct != 0 { return 0, errors.New("还有商品类别使用此类别,不能删除") } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if _, err = DeleteCategoryMap(ctx, db, categoryID); err != nil { dao.Rollback(db) return 0, err } if err = OnDeleteThing(ctx, db, nil, int64(categoryID), model.ThingTypeCategory); err != nil { dao.Rollback(db) return 0, err } if num, err = dao.DeleteEntityLogically(db, cat, utils.Params2Map(model.FieldJdSyncStatus, model.SyncFlagDeletedMask), userName, nil); err != nil { dao.Rollback(db) return 0, err } dao.Commit(db) _, err = CurVendorSync.SyncCategory(ctx, db, cat.ID, false, userName) } return num, err } func DeleteCategoryMap(ctx *jxcontext.Context, db *dao.DaoDB, categoryID int) (num int64, err error) { if db == nil { db = dao.GetDB() } catMap := &model.StoreSkuCategoryMap{} return dao.DeleteEntityLogically(db, catMap, map[string]interface{}{ model.FieldEbaiSyncStatus: model.SyncFlagDeletedMask, model.FieldMtwmSyncStatus: model.SyncFlagDeletedMask, }, ctx.GetUserName(), map[string]interface{}{ model.FieldCategoryID: categoryID, model.FieldDeletedAt: utils.DefaultTimeValue, }) } func GetSkuNames(ctx *jxcontext.Context, keyword string, isBySku bool, params map[string]interface{}, offset, pageSize int) (skuNamesInfo *SkuNamesInfo, err error) { db := dao.GetDB() sql := ` FROM sku_name t1 LEFT JOIN sku t2 ON t1.id = t2.name_id AND t2.deleted_at = ? LEFT JOIN sku_name_place_bind t3 ON t1.id = t3.name_id WHERE t1.deleted_at = ?` sqlParams := []interface{}{ utils.DefaultTimeValue, utils.DefaultTimeValue, } if keyword != "" { keywordLike := "%" + keyword + "%" sql += " AND (t1.name LIKE ? OR t1.prefix LIKE ? OR t2.comment LIKE ? OR t1.upc LIKE ?" sqlParams = append(sqlParams, keywordLike, keywordLike, keywordLike, keywordLike) if globals.IsUseThingMap { sql += " OR (SELECT COUNT(*) FROM thing_map tm WHERE tm.vendor_thing_id LIKE ? AND tm.thing_type = ? AND tm.thing_id = t2.id AND tm.deleted_at = ?) > 0" sqlParams = append(sqlParams, keywordLike, model.ThingTypeSku, utils.DefaultTimeValue) } if keywordInt64, err2 := strconv.ParseInt(keyword, 10, 64); err2 == nil { sql += " OR t1.id = ? OR t2.id = ? OR t1.category_id = ?" sqlParams = append(sqlParams, keywordInt64, keywordInt64, keywordInt64) if !globals.IsUseThingMap { sql += " OR t2.jd_id = ?" sqlParams = append(sqlParams, keywordInt64) } } sql += ")" } if params["isSpu"] != nil { sql += " AND t1.is_spu = ?" sqlParams = append(sqlParams, utils.Bool2Int(params["isSpu"].(bool))) } if params["nameID"] != nil { sql += " AND t1.id = ?" sqlParams = append(sqlParams, params["nameID"].(int)) } if params["nameIDs"] != nil { var nameIDs []int if err = utils.UnmarshalUseNumber([]byte(params["nameIDs"].(string)), &nameIDs); err != nil { return nil, err } if len(nameIDs) > 0 { sql += " AND t1.id IN (" + dao.GenQuestionMarks(len(nameIDs)) + ")" sqlParams = append(sqlParams, nameIDs) } } if params["categoryID"] != nil { cat := &model.SkuCategory{} cat.ID = params["categoryID"].(int) if err = dao.GetEntity(db, cat); err != nil { return nil, err } sql += " AND (t1.category_id = ?" sqlParams = append(sqlParams, cat.ID) if cat.Level == 1 { sql += " OR t1.category_id IN (SELECT id FROM sku_category WHERE parent_id = ?)" sqlParams = append(sqlParams, cat.ID) } sql += ")" } if params["skuCategoryID"] != nil { cat := &model.SkuCategory{} cat.ID = params["skuCategoryID"].(int) if err = dao.GetEntity(db, cat); err != nil { return nil, err } sql += " AND (t2.category_id = ?" sqlParams = append(sqlParams, cat.ID) if cat.Level == 1 { sql += " OR t2.category_id IN (SELECT id FROM sku_category WHERE parent_id = ?)" sqlParams = append(sqlParams, cat.ID) } sql += ")" } if params["vendorSkuIDs"] != nil { var vendorSkuIDs []int if err = utils.UnmarshalUseNumber([]byte(params["vendorSkuIDs"].(string)), &vendorSkuIDs); err != nil { return nil, err } if len(vendorSkuIDs) > 0 { sql += " AND t2.jd_id IN (" + dao.GenQuestionMarks(len(vendorSkuIDs)) + ")" sqlParams = append(sqlParams, vendorSkuIDs) } } if params["name"] != nil { sql += " AND t1.name LIKE ?" sqlParams = append(sqlParams, "%"+params["name"].(string)+"%") } if params["prefix"] != nil { sql += " AND t1.prefix LIKE ?" sqlParams = append(sqlParams, "%"+params["prefix"].(string)+"%") } if params["unit"] != nil { sql += " AND t1.unit = ?" sqlParams = append(sqlParams, params["unit"].(string)) } if placeCond := strings.ToUpper(utils.Interface2String(params["placeCond"])); placeCond == "AND" || placeCond == "OR" { sqlPlaceCond := "" if placeCond == "AND" { sqlPlaceCond += " AND ( 1 = 1" } else { sqlPlaceCond += " AND ( 1 = 0" } if params["placeCode"] != nil { sqlPlaceCond += " " + placeCond + " t3.place_code = ?" sqlParams = append(sqlParams, params["placeCode"].(int)) } if params["isGlobal"] != nil { if params["isGlobal"].(bool) { sqlPlaceCond += " " + placeCond + " t1.is_global = 1" } else { sqlPlaceCond += " " + placeCond + " t1.is_global = 0" } } if sqlPlaceCond != " AND ( 1 = 0" { sql += sqlPlaceCond + ")" } } if params["skuID"] != nil { sql += " AND t2.id = ?" sqlParams = append(sqlParams, params["skuID"].(int)) } if params["skuIDs"] != nil { var skuIDs []int if err = utils.UnmarshalUseNumber([]byte(params["skuIDs"].(string)), &skuIDs); err != nil { return nil, err } if len(skuIDs) > 0 { sql += " AND t2.id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")" sqlParams = append(sqlParams, skuIDs) } } if params["fromStatus"] != nil { fromStatus := params["fromStatus"].(int) toStatus := fromStatus if params["toStatus"] != nil { toStatus = params["toStatus"].(int) } sql += " AND t2.status >= ? AND t2.status <= ?" sqlParams = append(sqlParams, fromStatus, toStatus) } sql += ` GROUP BY t1.id, t1.created_at, t1.updated_at, t1.last_operator, t1.deleted_at, t1.prefix, t1.name, t1.brand_id, t1.category_id, t1.jd_category_id, t1.is_global, t1.unit, t1.price, t1.img, t1.img2, t1.status, t1.is_spu, t1.desc_img, t1.upc, t1.ex_prefix, t1.ex_prefix_begin, t1.ex_prefix_end` if isBySku { sql += `, t2.id` } sqlData := ` SELECT SQL_CALC_FOUND_ROWS t1.id, t1.created_at, t1.updated_at, t1.last_operator, t1.deleted_at, t1.prefix, t1.name, t1.brand_id, t1.category_id, t1.jd_category_id, t1.is_global, t1.unit, t1.price, t1.img, t1.img2, t1.status, t1.is_spu, t1.desc_img, t1.upc, t1.jd_id, t1.jd_sync_status, t1.ex_prefix, t1.ex_prefix_begin, t1.ex_prefix_end, CONCAT("[", GROUP_CONCAT(DISTINCT CONCAT('{"id":', t2.id, ',"comment":"', t2.comment, '","status":', t2.status, ',"createdAt":"', CONCAT(REPLACE(t2.created_at," ","T"),"+08:00"), '","updatedAt":"', CONCAT(REPLACE(t2.updated_at," ","T"),"+08:00"), '","lastOperator":"', t2.last_operator, '","specQuality":', t2.spec_quality, ',"specUnit":"', t2.spec_unit, '","weight":', t2.weight, ',"jdID":', t2.jd_id, ',"categoryID":', t2.category_id, ',"nameID":', t2.name_id, ',"jdID":', t2.jd_id, ',"jdSyncStatus":', t2.jd_sync_status, ', "seq":', t2.seq, "}")), "]") skus_str, CONCAT("[", GROUP_CONCAT(DISTINCT t3.place_code), "]") places_str ` + sql + ` ORDER BY MIN(t2.seq), t1.id DESC LIMIT ? OFFSET ?` pageSize = jxutils.FormalizePageSize(pageSize) offset = jxutils.FormalizePageOffset(offset) sqlParams = append(sqlParams, pageSize, offset) skuNamesInfo = &SkuNamesInfo{} dao.Begin(db) // todo 这里用事务的原因是,SQL_CALC_FOUND_ROWS会出错 defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() // globals.SugarLogger.Debug(sqlData) // globals.SugarLogger.Debug(utils.Format4Output(sqlParams, false)) if err = dao.GetRows(db, &skuNamesInfo.SkuNames, sqlData, sqlParams...); err == nil { skuNamesInfo.TotalCount = dao.GetLastTotalRowCount(db) dao.Commit(db) var skuIDs []int for _, skuName := range skuNamesInfo.SkuNames { skuName.FullName = jxutils.ComposeSkuName(skuName.Prefix, skuName.Name, "", "", 0, "", 0, skuName.ExPrefix, skuName.ExPrefixBegin, skuName.ExPrefixEnd) if skuName.SkusStr != "" { if err = utils.UnmarshalUseNumber([]byte(skuName.SkusStr), &skuName.Skus); err != nil { dao.Rollback(db) return nil, err } for _, v := range skuName.Skus { skuIDs = append(skuIDs, v.ID) } } if skuName.PlacesStr != "" { if err = utils.UnmarshalUseNumber([]byte(skuName.PlacesStr), &skuName.Places); err != nil { dao.Rollback(db) return nil, err } } } if len(skuIDs) > 0 { thingMapMap, err2 := dao.GetThingMapMap(db, model.ThingTypeSku, nil, skuIDs) if err = err2; err == nil { for _, skuName := range skuNamesInfo.SkuNames { for _, v := range skuName.Skus { v.MapList = thingMapMap[int64(v.ID)] } } } } } else { dao.Rollback(db) } return skuNamesInfo, err } func CheckHasSensitiveWord(word string) (bool, error) { if hasSensitiveWord, sensitiveWord := IsSensitiveWordInList(word); hasSensitiveWord { return true, errors.New(fmt.Sprintf("不能包含敏感词:[%s]", sensitiveWord)) } return false, nil } func IsSensitiveWordInList(str string) (bool, string) { wordList, err := dao.GetSensitiveWordList() if err == nil { for _, value := range wordList { keyWord := value.Word checkHas := strings.Contains(str, keyWord) if checkHas { return true, keyWord } } } return false, "" } func AddSkuName(ctx *jxcontext.Context, skuNameExt *model.SkuNameExt, userName string) (outSkuNameExt *model.SkuNameExt, err error) { if skuNameExt.CategoryID == 0 { return nil, errors.New("CategoryID不能为空") } if len(skuNameExt.Skus) == 0 { return nil, errors.New("创建SKU NAME时必须至少创建一个SKU") } else if skuNameExt.Unit != model.SpecialUnit { if len(skuNameExt.Skus) != 1 { return nil, errors.New("不为份的SKU NAME只能有一个SKU") } } skuNameExt.Name = utils.TrimBlankChar(skuNameExt.Name) if hasSensitiveWord, err := CheckHasSensitiveWord(skuNameExt.Name); hasSensitiveWord { return nil, err } if utils.Pointer2String(skuNameExt.Upc) == "" { skuNameExt.Upc = nil } db := dao.GetDB() // if skuNameExt.Upc != "" { // err = dao.GetEntity(db, &skuNameExt.SkuName, "Upc") // if err == nil { // return nil, fmt.Errorf("UPC:%s重复", skuNameExt.Upc) // } else if !dao.IsNoRowsError(err) { // return nil, err // } // err = nil // } skuNameExt.SkuName.Status = model.SkuStatusNormal if skuNameExt.IsSpu == 1 { return nil, fmt.Errorf("不允许创建多规格商品") skuNameExt.SkuName.JdSyncStatus = model.SyncFlagNewMask } if skuNameExt.Unit == model.SpecialUnit { skuNameExt.SpecQuality = float32(model.SpecialSpecQuality) skuNameExt.SpecUnit = model.SpecialSpecUnit } else { skuNameExt.SpecQuality = skuNameExt.Skus[0].SpecQuality skuNameExt.SpecUnit = skuNameExt.Skus[0].SpecUnit } picType := true for _, imgName := range []string{skuNameExt.Img, skuNameExt.Img2} { if imgName != "" { dataRes, err2 := datares.TryRegisterDataResource(ctx, skuNameExt.Name, imgName, model.ImgTypeMain, false) if dataRes.ResourceType == model.MimeTypeJpeg || dataRes.ResourceType == model.MimeTypePng { picType = false } if err = err2; err != nil { return nil, err } } } if picType { return nil, fmt.Errorf("商品图片应至少包含一张非gif格式的图片!") } if skuNameExt.DescImg != "" { dataRes, err2 := datares.TryRegisterDataResource(ctx, skuNameExt.Name+"desc", skuNameExt.DescImg, model.ImgTypeDesc, false) if dataRes.ResourceType == model.MimeTypeGif { return nil, fmt.Errorf("商品详情图片不能上传gif格式的图片!") } if err = err2; err != nil { return nil, err } } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() dao.WrapAddIDCULDEntity(&skuNameExt.SkuName, userName) if err = dao.CreateEntity(db, &skuNameExt.SkuName); err != nil { dao.Rollback(db) return nil, err } if err = OnCreateThing(ctx, db, nil, int64(skuNameExt.SkuName.ID), model.ThingTypeSkuName); err != nil { dao.Rollback(db) return nil, err } for _, v := range skuNameExt.Skus { sku := v.Sku dao.WrapAddIDCULDEntity(sku, userName) sku.NameID = skuNameExt.ID sku.JdSyncStatus = model.SyncFlagNewMask sku.JdID = 0 if err = dao.CreateEntity(db, sku); err != nil { dao.Rollback(db) return nil, err } if err = OnCreateThing(ctx, db, nil, int64(sku.ID), model.ThingTypeSku); err != nil { dao.Rollback(db) return nil, err } } for _, placeCode := range skuNameExt.Places { placeBind := &model.SkuNamePlaceBind{} dao.WrapAddIDCULEntity(placeBind, userName) placeBind.NameID = skuNameExt.ID placeBind.PlaceCode = placeCode if err = dao.CreateEntity(db, placeBind); err != nil { dao.Rollback(db) return nil, err } } dao.Commit(db) tmpInfo, err := GetSkuNames(ctx, "", false, utils.Params2Map("nameID", skuNameExt.SkuName.ID), 0, 1) if err != nil { return nil, err } if tmpInfo.TotalCount != 1 { return nil, ErrEntityNotExist } outSkuNameExt = tmpInfo.SkuNames[0] _, err = CurVendorSync.SyncSku(ctx, db, outSkuNameExt.SkuName.ID, -1, false, false, userName) return outSkuNameExt, err } func UpdateSkuName(ctx *jxcontext.Context, nameID int, payload map[string]interface{}) (num int64, err error) { userName := ctx.GetUserName() skuName := &model.SkuName{} skuName.ID = nameID db := dao.GetDB() if err = dao.GetEntity(db, skuName); err != nil { return 0, err } if payload["name"] != nil { newSkuName := utils.TrimBlankChar(utils.Interface2String(payload["name"])) if hasSensitiveWord, err := CheckHasSensitiveWord(newSkuName); hasSensitiveWord { return 0, err } payload["name"] = newSkuName } delete(payload, "isSpu") delete(payload, "seq") valid := dao.StrictMakeMapByStructObject(payload, skuName, userName) valid = utils.RemoveGeneralMapKeys(valid, model.FieldSpecQuality, model.FieldSpecUnit) _, hasPlaces := payload["places"] if len(valid) > 0 || hasPlaces { if upc, _ := valid["Upc"].(string); upc == "" { valid["Upc"] = nil } globals.SugarLogger.Debugf("UpdateSkuName valid:%s", utils.Format4Output(valid, false)) // if upc, _ := valid["Upc"].(string); upc != "" { // skuName := &model.SkuName{ // Upc: upc, // } // err = dao.GetEntity(db, skuName, "Upc") // if err == nil { // return 0, fmt.Errorf("UPC:%s重复", upc) // } else if !dao.IsNoRowsError(err) { // return 0, err // } // err = nil // } for _, imgName := range []string{"img", "img2"} { if valid[imgName] != nil { if imgStr := utils.Interface2String(valid[imgName]); imgStr != "" { _, err2 := datares.TryRegisterDataResource(ctx, skuName.Name, valid[imgName].(string), model.ImgTypeMain, true) if err = err2; err != nil { return 0, err } } } } if valid["descImg"] != nil { descImg := valid["descImg"].(string) if descImg != "" { _, err2 := datares.TryRegisterDataResource(ctx, skuName.Name+"_desc", descImg, model.ImgTypeDesc, false) if err = err2; err != nil { return 0, err } } } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() valid[model.FieldJdSyncStatus] = model.SyncFlagModifiedMask | skuName.JdSyncStatus if num, err = dao.UpdateEntityLogically(db, skuName, valid, userName, nil); err != nil { dao.Rollback(db) return 0, err } if err = OnUpdateThing(ctx, db, nil, int64(nameID), model.ThingTypeSkuName); err != nil { dao.Rollback(db) return 0, err } if utils.Interface2Int64WithDefault(payload["isGlobal"], 0) == 0 && payload["places"] != nil { if places, ok := payload["places"].([]interface{}); ok { if _, err = dao.DeleteSkuNamePlace(db, nameID, nil); err != nil { dao.Rollback(db) return 0, err } for _, placeCode := range places { placeBind := &model.SkuNamePlaceBind{} placeBind.PlaceCode = int(utils.Interface2Int64WithDefault(placeCode, 0)) if placeBind.PlaceCode > 0 { dao.WrapAddIDCULEntity(placeBind, userName) placeBind.NameID = nameID err = dao.CreateEntity(db, placeBind) } else { dao.Rollback(db) return 0, errors.New("地点代码非法") } } } } skuList, err2 := dao.GetSkus(db, nil, []int{nameID}, nil, nil) if err = err2; err == nil { for _, v := range skuList { sku := &v.Sku sku.JdSyncStatus |= model.SyncFlagModifiedMask sku.LastOperator = userName sku.UpdatedAt = time.Now() if _, err = dao.UpdateEntity(db, sku); err != nil { dao.Rollback(db) return 0, err } if err = OnUpdateThing(ctx, db, nil, int64(v.ID), model.ThingTypeSku); err != nil { dao.Rollback(db) return 0, err } } } skuIDs, err2 := dao.GetSkuIDByNames(db, []int{nameID}) if err = err2; err != nil { dao.Rollback(db) return 0, err } if len(skuIDs) > 0 { if _, err = SetStoreSkuSyncStatus2(db, nil, partner.GetSingleStoreVendorIDs(), skuIDs, model.SyncFlagModifiedMask); err != nil { dao.Rollback(db) return 0, err } } dao.Commit(db) errList := errlist.New() errList.AddErr(err) _, err = CurVendorSync.SyncSku(ctx, db, nameID, -1, false, false, userName) errList.AddErr(err) err = errList.GetErrListAsOne() } return num, err } func SetStoreSkuSyncStatus2(db *dao.DaoDB, storeIDs []int, vendorIDs, skuIDs []int, syncStatus int) (num int64, err error) { for _, vendorID := range vendorIDs { num2, err2 := dao.SetStoreSkuSyncStatus(db, vendorID, storeIDs, skuIDs, syncStatus) if err = err2; err != nil { return 0, err } num += num2 } return num, nil } func DeleteSkuName(ctx *jxcontext.Context, nameID int, userName string) (num int64, err error) { db := dao.GetDB() dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if _, err := dao.DeleteSkuNamePlace(db, nameID, nil); err != nil { dao.Rollback(db) return 0, err } if _, err = DeleteStoreSku(ctx, db, nameID, 0); err != nil { dao.Rollback(db) return 0, err } skuList, err2 := dao.GetSkus(db, nil, []int{nameID}, nil, nil) if err = err2; err == nil { for _, v := range skuList { sku := &v.Sku if err = OnDeleteThing(ctx, db, nil, int64(v.ID), model.ThingTypeSku); err != nil { dao.Rollback(db) return 0, err } if _, err = dao.DeleteEntityLogically(db, sku, map[string]interface{}{ model.FieldJdSyncStatus: model.SyncFlagDeletedMask, model.FieldStatus: model.SkuStatusDeleted, }, userName, nil); err != nil { dao.Rollback(db) return 0, err } } } if err = OnDeleteThing(ctx, db, nil, int64(nameID), model.ThingTypeSkuName); err != nil { dao.Rollback(db) return 0, err } skuName := &model.SkuName{} skuName.ID = nameID if num, err = dao.DeleteEntityLogically(db, skuName, map[string]interface{}{ model.FieldJdSyncStatus: model.SyncFlagDeletedMask, model.FieldStatus: model.SkuStatusDeleted, }, userName, nil); err != nil { dao.Rollback(db) return 0, err } dao.Commit(db) if len(skuList) > 0 { _, err = CurVendorSync.SyncSku(ctx, db, skuName.ID, -1, false, false, userName) } return num, err } func AddSku(ctx *jxcontext.Context, nameID int, sku *model.Sku, userName string) (outSkuNameExt *model.SkuNameExt, err error) { db := dao.GetDB() skuName := &model.SkuName{} skuName.ID = nameID if err = dao.GetEntity(db, skuName); err != nil { return nil, err } if skuName.Unit != model.SpecialUnit { return nil, errors.New("不为份的SKU NAME只能有一个SKU") } dao.WrapAddIDCULDEntity(sku, userName) sku.JdSyncStatus = model.SyncFlagNewMask sku.JdID = 0 sku.NameID = nameID dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if err = dao.CreateEntity(db, sku); err != nil { dao.Rollback(db) return nil, err } if err = OnCreateThing(ctx, db, nil, int64(sku.ID), model.ThingTypeSku); err != nil { dao.Rollback(db) return nil, err } dao.Commit(db) result, err2 := GetSkuNames(ctx, "", false, utils.Params2Map("skuID", sku.ID), 0, 0) if err = err2; err == nil { if result.TotalCount == 1 { outSkuNameExt = result.SkuNames[0] _, err = CurVendorSync.SyncSku(ctx, db, outSkuNameExt.SkuName.ID, sku.ID, false, false, userName) } else { err = ErrEntityNotExist } } return outSkuNameExt, err } func UpdateSku(ctx *jxcontext.Context, skuID int, payload map[string]interface{}) (num int64, err error) { userName := ctx.GetUserName() sku := &model.Sku{} sku.ID = skuID db := dao.GetDB() if err = dao.GetEntity(db, sku); err != nil { return 0, err } valid := dao.StrictMakeMapByStructObject(payload, sku, userName) if len(valid) > 0 { // globals.SugarLogger.Debug(utils.Format4Output(valid, false)) dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() maskValue := int8(model.SyncFlagModifiedMask) if valid["specQuality"] != nil || valid["specUnit"] != nil { maskValue |= model.SyncFlagSpecMask } valid[model.FieldJdSyncStatus] = maskValue | sku.JdSyncStatus if num, err = dao.UpdateEntityLogically(db, sku, valid, userName, nil); err != nil || num == 0 { dao.Rollback(db) if err == nil { err = ErrEntityNotExist } return 0, err } if _, err = dao.ExecuteSQL(db, ` UPDATE sku_name t1 JOIN sku t2 ON t1.id = t2.name_id SET t1.spec_quality = t2.spec_quality, t1.spec_unit = t2.spec_unit WHERE t1.deleted_at = ? AND t2.id = ? AND t1.unit <> ? `, utils.DefaultTimeValue, skuID, model.SpecialUnit); err != nil { dao.Rollback(db) if err == nil { err = ErrEntityNotExist } return 0, err } if err = OnUpdateThing(ctx, db, nil, int64(skuID), model.ThingTypeSku); err != nil { dao.Rollback(db) return 0, err } dao.Commit(db) if _, err = SetStoreSkuSyncStatus2(db, nil, partner.GetSingleStoreVendorIDs(), []int{skuID}, model.SyncFlagModifiedMask); err == nil { if maskValue&model.SyncFlagSpecMask != 0 { err = refreshStoreSkuPrice(ctx, db, skuID) } } errList := errlist.New() errList.AddErr(err) _, err = CurVendorSync.SyncSku(ctx, db, -1, sku.ID, false, false, userName) errList.AddErr(err) err = errList.GetErrListAsOne() } return num, err } func refreshStoreSkuPrice(ctx *jxcontext.Context, db *dao.DaoDB, skuID int) (err error) { dao.Begin(db) defer func() { if r := recover(); r != nil || err != nil { dao.Rollback(db) if r != nil { panic(r) } } }() for vendorID := range partner.PurchasePlatformHandlers { storeSkuList, err := dao.GetStoreSkus2(db, vendorID, 0, []int{skuID}, false) if err == nil { for _, v := range storeSkuList { v.Price = int64(jxutils.CaculateSkuPrice(int(v.UnitPrice), v.SpecQuality, v.SpecUnit, v.Unit)) storeSku := &model.StoreSkuBind{} storeSku.ID = v.BindID if _, err = dao.UpdateEntityLogically(db, storeSku, map[string]interface{}{ "Price": v.Price, dao.GetSyncStatusStructField(model.VendorNames[vendorID]): v.SkuSyncStatus | model.SyncFlagPriceMask, }, ctx.GetUserName(), nil); err != nil { return err } } } } dao.Commit(db) return err } func DeleteSku(ctx *jxcontext.Context, skuID int, userName string) (num int64, err error) { db := dao.GetDB() dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if _, err = DeleteStoreSku(ctx, db, 0, skuID); err != nil { dao.Rollback(db) return 0, err } if err = OnDeleteThing(ctx, db, nil, int64(skuID), model.ThingTypeSku); err != nil { dao.Rollback(db) return 0, err } sku := &model.Sku{} sku.ID = skuID if num, err = dao.DeleteEntityLogically(db, sku, map[string]interface{}{ model.FieldStatus: model.SkuStatusDeleted, model.FieldJdSyncStatus: model.SyncFlagDeletedMask, }, userName, nil); err != nil { dao.Rollback(db) return 0, err } dao.Commit(db) if num == 1 { _, err = CurVendorSync.SyncSku(ctx, db, -1, sku.ID, false, false, userName) } return num, err } func DeleteStoreSku(ctx *jxcontext.Context, db *dao.DaoDB, nameID, skuID int) (num int64, err error) { if db == nil { db = dao.GetDB() } sql := ` SELECT * FROM sku t1 WHERE 1 = 1 ` sqlParams := []interface{}{} if nameID > 0 { sql += " AND t1.name_id = ?" sqlParams = append(sqlParams, nameID) } else { sql += " AND t1.id = ?" sqlParams = append(sqlParams, skuID) } var skuList []*model.Sku if err = dao.GetRows(db, &skuList, sql, sqlParams); err == nil { num = int64(len(skuList)) for _, v := range skuList { storeSkuBind := &model.StoreSkuBind{} _, err = dao.DeleteEntityLogically(db, storeSkuBind, map[string]interface{}{ model.FieldJdSyncStatus: model.SyncFlagDeletedMask, model.FieldMtwmSyncStatus: model.SyncFlagDeletedMask, model.FieldEbaiSyncStatus: model.SyncFlagDeletedMask, }, ctx.GetUserName(), map[string]interface{}{ model.FieldSkuID: v.ID, model.FieldDeletedAt: utils.DefaultTimeValue, }) if err != nil { break } } } return num, err } func AddSkuNamePlace(ctx *jxcontext.Context, nameID, placeCode int, userName string) (outPlaceBind *model.SkuNamePlaceBind, err error) { db := dao.GetDB() placeBind := &model.SkuNamePlaceBind{ NameID: nameID, PlaceCode: placeCode, } dao.WrapAddIDCULEntity(placeBind, userName) dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if err = dao.CreateEntity(db, placeBind); err != nil { dao.Rollback(db) return nil, err } if err = OnUpdateThing(ctx, db, nil, int64(nameID), model.ThingTypeSkuName); err != nil { dao.Rollback(db) return nil, err } dao.Commit(db) _, err = CurVendorSync.SyncSku(ctx, db, nameID, -1, false, false, userName) return placeBind, err } func DeleteSkuNamePlace(ctx *jxcontext.Context, nameID, placeCode int, userName string) (num int64, err error) { db := dao.GetDB() placeBind := &model.SkuNamePlaceBind{} placeBind.NameID = nameID placeBind.PlaceCode = placeCode dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if num, err = dao.DeleteEntity(db, placeBind, model.FieldNameID, model.FieldPlaceCode); err != nil || num == 0 { dao.Rollback(db) if err == nil { err = ErrEntityNotExist } return 0, err } if err = OnUpdateThing(ctx, db, nil, int64(nameID), model.ThingTypeSkuName); err != nil { dao.Rollback(db) return 0, err } dao.Commit(db) _, err = CurVendorSync.SyncSku(ctx, db, nameID, -1, false, false, userName) return num, err } // func GetVendorSku(ctx *jxcontext.Context, vendorID int, vendorOrgCode, vendorSkuID string) (skuNameInfo *model.SkuNameExt, err error) { // if handler := CurVendorSync.GetMultiStoreHandler(vendorID); handler != nil { // return handler.ReadSku(ctx, vendorOrgCode, vendorSkuID) // } // return nil, ErrCanNotFindVendor // } func SortCategorySkus(ctx *jxcontext.Context, catID int, skuIDList []int) (err error) { db := dao.GetDB() userName := ctx.GetUserName() var skuList []*model.Sku if skuList, err = dao.GetSkuByCats(db, []int{catID}); err == nil && len(skuList) > 0 { if len(skuList) != len(skuIDList) { return errors.New("商品数量不匹配!") } skuIDMap := make(map[int]int) for index, id := range skuIDList { skuIDMap[id] = index + 1 } for _, value := range skuList { if _, ok := skuIDMap[value.ID]; !ok { return errors.New("商品数据不匹配!") } } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() nameIDList := []int{} for _, value := range skuList { seq := skuIDMap[value.ID] if value.Seq != seq { kvs := map[string]interface{}{ model.FieldSkuSeq: seq, //model.FieldJdSyncStatus: (value.JdSyncStatus | model.SyncFlagModifiedMask), } dao.UpdateEntityLogically(db, value, kvs, userName, nil) nameIDList = append(nameIDList, value.NameID) } } //_, err = CurVendorSync.SyncSkus(ctx, db, nameIDList, []int{}, false, false, userName) if err == nil && len(nameIDList) > 0 { dao.Commit(db) skuIDs, err2 := dao.GetSkuIDByNames(db, nameIDList) if err = err2; err == nil && len(skuIDs) > 0 { _, err = SetStoreSkuSyncStatus2(db, nil, partner.GetSingleStoreVendorIDs(), skuIDs, model.SyncFlagModifiedMask) } } else { dao.Rollback(db) } } return err } func GetJdUpcCodeByName(ctx *jxcontext.Context, name, upcCode string) (productInfos []*jdapi.ProductInfo, err error) { var ( pageNo = 5 pageSize = 30 pageNoList []int ) if name == "" && upcCode == "" { return nil, fmt.Errorf("至少输入一个条件查询,商品名或者upc码!") } for i := 1; i < pageNo+1; i++ { pageNoList = append(pageNoList, i) } task := tasksch.NewParallelTask("获取京东商品", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { pageNum := batchItemList[0].(int) productInfo, err := api.JdAPI.GetJdUpcCodeByName(name, upcCode, pageNum, pageSize) if err != nil { return retVal, err } for _, v := range productInfo { _, name, _, specUnit, _, specQuality := jxutils.SplitSkuName(v.OriginalName) v.Name = name v.SpecQuality = specQuality v.SpecUnit = specUnit } retVal = productInfo return retVal, err }, pageNoList) tasksch.HandleTask(task, nil, true).Run() productInfoInterface, err := task.GetResult(0) for _, v := range productInfoInterface { productInfos = append(productInfos, v.(*jdapi.ProductInfo)) } return productInfos, err } func UpdateSkuNamesExPrefix(ctx *jxcontext.Context, nameIDs []int, exPrefix, fromTime, toTime string, isAsync, isContinueWhenError bool) (hint string, err error) { var ( fromTimeP time.Time toTimeP time.Time db = dao.GetDB() ) if fromTime != "" { fromTimeP = utils.Time2Date(utils.Str2Time(fromTime)) } if toTime != "" { toTimeP = utils.Time2Date(utils.Str2Time(toTime)) } if toTimeP.Before(fromTimeP) { return "", fmt.Errorf("结束时间不可以小于开始时间!开始时间:[%v],结束时间:[%v]", fromTimeP, toTimeP) } task := tasksch.NewParallelTask("批量设置商品前缀", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(isContinueWhenError), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { step := batchItemList[0].(int) now := utils.Time2Date(time.Now()) switch step { case 0: task := tasksch.NewParallelTask("批量设置商品前缀", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { nameID := batchItemList[0].(int) payload := map[string]interface{}{ "exPrefix": exPrefix, "exPrefixBegin": fromTimeP, "exPrefixEnd": toTimeP, } if now.Sub(toTimeP) <= 0 && now.Sub(fromTimeP) >= 0 { _, err = UpdateSkuName(ctx, nameID, payload) } else if now.Sub(fromTimeP) > 0 && now.Sub(toTimeP) > 0 { payload["exPrefixBegin"] = nil payload["exPrefixEnd"] = nil _, err = UpdateSkuName(ctx, nameID, payload) } else { skuName := &model.SkuName{ ExPrefix: exPrefix, ExPrefixBegin: &fromTimeP, ExPrefixEnd: &toTimeP, } skuName.ID = nameID skuName.LastOperator = ctx.GetLoginID() skuName.UpdatedAt = time.Now() dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() _, err = dao.UpdateEntity(db, skuName, "ExPrefix", "ExPrefixBegin", "ExPrefixEnd", "LastOperator", "UpdatedAt") dao.Commit(db) } return retVal, err }, nameIDs) tasksch.HandleTask(task, nil, true).Run() _, err = task.GetResult(0) case 1: if (now.Sub(toTimeP) <= 0 && now.Sub(fromTimeP) >= 0) || (now.Sub(fromTimeP) > 0 && now.Sub(toTimeP) > 0) { var skuIDs []int skuList, err2 := dao.GetSkus(db, nil, nameIDs, nil, nil) if err = err2; err == nil { if len(skuList) > 0 { for _, v := range skuList { skuIDs = append(skuIDs, v.ID) } CurVendorSync.SyncStoresSkus2(ctx, db, partner.GetSingleStoreVendorIDs(), nil, false, skuIDs, nil, 0, true, true) } } } } return retVal, err }, []int{0, 1}) tasksch.HandleTask(task, nil, true).Run() if !isAsync { _, err = task.GetResult(0) hint = "1" } else { hint = task.GetID() } return hint, err } func SetSingleStoreSkuSyncModifyStatus(db *dao.DaoDB, vendorIDs []int) (err error) { _, err = dao.UpdateStoreSkuBindSyncStatusForExPrefix(db, vendorIDs) return err } func SetMultiStoreSkuSyncModifyStatus(db *dao.DaoDB, vendorIDs []int) (err error) { _, err = dao.UpdateSkuSyncStatusForExPrefix(db, vendorIDs) return err } func DeleteSkuNameExPrefixOverdue(db *dao.DaoDB) (err error) { _, err = dao.DeleteSkuNameExPrefixOverdue(db) return err }