From 004a7d664607c843c165a6651255e8932e2dbed6 Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Fri, 22 Aug 2025 18:37:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=BC=E5=85=A5=E5=A4=9A=E8=A7=84=E6=A0=BC?= =?UTF-8?q?=E5=8C=B9=E9=85=8D=E5=8A=9F=E8=83=BD=E6=96=B0=E5=A2=9E=EF=BC=8C?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=B8=85=E9=86=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/product/ShopProductSpecItem.java | 3 + .../common/modules/sync/SyncStoreSpecs.java | 62 ++ .../suisung/mall/common/utils/JiebaUtils.java | 133 +++- .../mall/common/utils/ProductTitleUtil.java | 15 +- mall-common/src/main/resources/dict.txt | 104 ++- .../impl/EsProductImageServiceImpl.java | 8 +- .../number/service/ShopNumberSeqService.java | 4 + .../impl/ShopNumberSeqServiceImpl.java | 37 +- .../admin/ShopPageAppController.java | 5 +- .../admin/ShopPageBaseController.java | 5 +- .../shop/sync/exelModel/BrandModelExcel.java | 2 +- .../shop/sync/exelModel/SxGoosModelExcel.java | 7 + .../sync/keymanage/ProductSpecManager.java | 188 ++++++ .../mall/shop/sync/keymanage/RedisKey.java | 3 + .../sync/listen/ShopBatchSubmitListener.java | 204 ++++-- .../sync/mapper/SyncStoreSpecsMapper.java | 22 + .../sync/service/SyncStoreSpecsService.java | 9 + .../impl/ProductMappingServiceImpl.java | 3 +- .../impl/ShopSyncImportServiceImpl.java | 10 +- .../impl/SyncStoreSpecsServiceImpl.java | 593 ++++++++++++++++++ .../mapper/sync/SyncStoreSpecsMapper.xml | 38 ++ sql/shop/dev/20250819_ddl.sql | 13 + 22 files changed, 1383 insertions(+), 85 deletions(-) create mode 100644 mall-common/src/main/java/com/suisung/mall/common/modules/sync/SyncStoreSpecs.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/ProductSpecManager.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/SyncStoreSpecsMapper.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncStoreSpecsService.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncStoreSpecsServiceImpl.java create mode 100644 mall-shop/src/main/resources/mapper/sync/SyncStoreSpecsMapper.xml create mode 100644 sql/shop/dev/20250819_ddl.sql diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/product/ShopProductSpecItem.java b/mall-common/src/main/java/com/suisung/mall/common/modules/product/ShopProductSpecItem.java index 7d5d02d0..ad69328d 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/product/ShopProductSpecItem.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/product/ShopProductSpecItem.java @@ -72,4 +72,7 @@ public class ShopProductSpecItem implements Serializable { @TableField(exist=false) private boolean isUpdate; + @ApiModelProperty(value = "规格名称") + @TableField(exist=false) + private String specName; } diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/sync/SyncStoreSpecs.java b/mall-common/src/main/java/com/suisung/mall/common/modules/sync/SyncStoreSpecs.java new file mode 100644 index 00000000..8fe0ed0e --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/sync/SyncStoreSpecs.java @@ -0,0 +1,62 @@ +package com.suisung.mall.common.modules.sync; + +import com.baomidou.mybatisplus.annotation.*; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.Date; + +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@NoArgsConstructor +@TableName("sycn_store_specs") +@ApiModel("多规格数据表,用来执行导入的规格后续操作") +public class SyncStoreSpecs { + @TableId(value = "sycn_specs_id", type = IdType.AUTO) + @ApiModelProperty(value = "多规格编号", required = true, example = "10001") + private Long sycnSpecsId; + + @TableField(value = "product_number",updateStrategy = FieldStrategy.NOT_EMPTY) + @ApiModelProperty(value = "条形码", required = true, example = "10001") + private String productNumber; + + @TableField(value = "json_specs",updateStrategy = FieldStrategy.NOT_EMPTY) + @ApiModelProperty( + value = "多规格JSON数据,格式如:[{\"spec\": [{\"name\": \"颜色\",\"value\": \"红色\"},{\"name\": \"尺寸\",\"value\": \"10\"}],\"priceAndstock\": {\"price\": 100,\"stock\": 10}}]", + example = "[{\"spec\": [{\"name\": \"颜色\",\"value\": \"红色\"},{\"name\": \"尺寸\",\"value\": \"10\"}],\"priceAndstock\": {\"price\": 100,\"stock\": 10}}]" + ) + private String jsonSpecs; + + @TableField(value = "store_id",updateStrategy = FieldStrategy.NOT_EMPTY) + @ApiModelProperty(value = "店铺编号", required = true, example = "2001") + private Integer storeId; + + @TableField(value = "is_deal",updateStrategy = FieldStrategy.NOT_EMPTY) + @ApiModelProperty(value = "是否处理完成(1:是, 0:否)", required = true, example = "0") + private String isDeal; + + @TableField(value = "created_at",updateStrategy = FieldStrategy.NOT_EMPTY) + @ApiModelProperty(value = "新建时间", example = "2023-01-01T12:00:00") + private Date createdAt; + + @TableField(value = "updated_at",updateStrategy = FieldStrategy.NOT_EMPTY) + @ApiModelProperty(value = "更新时间", example = "2023-01-02T14:30:00") + private Date updatedAt; + + @ApiModelProperty(value = "分类id") + @TableField(exist = false) + private Integer categoryId; + + @ApiModelProperty(value = "产品id") + @TableField(exist = false) + private Long productId; + + @ApiModelProperty(value = "产品itemId") + @TableField(exist = false) + private Long itemId; +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/JiebaUtils.java b/mall-common/src/main/java/com/suisung/mall/common/utils/JiebaUtils.java index a3472540..c4ae4d9c 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/JiebaUtils.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/JiebaUtils.java @@ -47,9 +47,10 @@ public class JiebaUtils { private static final Pattern UNIT_REGEX = Pattern.compile("\\d+[.]?\\d*[a-zA-Z]+"); // 匹配500ml/1.8L private static final Pattern SPEC_REGEX = Pattern.compile("\\d+[a-zA-Z]+[*]\\d+"); // 匹配500L*10 private static final Pattern MIXED_REGEX = Pattern.compile("[a-zA-Z]+[-]?\\d+"); // 匹配RSCW-1949 - private static final Pattern DIMENSION_REGEX = Pattern.compile("\\d+(?:\\\\.\\\\d+)?[\\\\u4e00-\\\\u9fa5a-zA-Z]+"); // 匹配维度(如2.0*2.3) - private static final Pattern UNIT_CHN_REGEX = Pattern.compile("([0-9零一二三四五六七八九十百千万亿]+)(条|个|卷)\\b");//匹配只有数字+单位的,如牛油果2个 - private static final Pattern UNIT_EVERY_REGEX = Pattern.compile("([0-9零一二三四五六七八九十百千万亿]+)([个条份根盒包])(?:\\s*([0-9]+)(g|克|ml|毫升)|\\s*/([袋箱盒份]))");//匹配商品名称+数量+数量单位+重量的 如牛油果2个150克 + private static final Pattern DIMENSION_REGEX = Pattern.compile("\\d+(?:\\\\.\\\\d+)?[\\u4e00-\\u9fa5a-zA-Z]+"); // 匹配维度(如2.0*2.3) + private static final Pattern UNIT_CHN_REGEX = Pattern.compile("([0-9零一二三四五六七八九十百千万亿]+|\\d*\\.?\\d+)\\s*(g|条|个|卷|kg|L)+\\*\\b");//匹配只有数字+单位的,如牛油果2个 + private static final Pattern UNIT_CHN_EVERY_REGEX = Pattern.compile("([0-9零一二三四五六七八九十百千万亿]+|\\d*\\.?\\d+)\\s*(g|条|个|卷|kg|L)+\\s*/([袋箱盒份条])");//匹配只有数字+单位的,如牛油果2个 + private static final Pattern UNIT_EVERY_REGEX = Pattern.compile("([0-9零一二三四五六七八九十百千万亿]+)([个条份根盒包])(?:\\s*([0-9]+)(g|克|ml|毫升)|\\s*/([袋箱盒份条]))");//匹配商品名称+数量+数量单位+重量的 如牛油果2个150克 //private static final Pattern DIMENSION_REGEX = Pattern.compile("([\\u4e00-\\u9fa5]+)(\\d+\\.?\\d*\\*\\d+\\.?\\d*(?:米)?)([\\u4e00-\\u9fa5]+)"); private static final Map PROTECT_PATTERNS = new HashMap(){{ put(PROTECT_UNIT,UNIT_REGEX); @@ -65,6 +66,10 @@ public class JiebaUtils { put("dot","\\."); }}; + public static final Set filterWords=Collections.unmodifiableSet(new HashSet<>(Arrays.asList( + "袋","/","包","盒","个","箱","*","瓶","条","一级","木盒","特级","二级","条盒","礼盒","三级","整袋","塑料","把","整件" + ))); + private static void loadUserDict() { // 方法1:通过文件加载 @@ -123,14 +128,30 @@ public class JiebaUtils { boolean unit = isUnitShop(text); if(special&&unit){ Matcher matcher = UNIT_EVERY_REGEX.matcher(text); + String original =""; + String original2 =""; if(matcher.find()) { - String original = matcher.group(); - // 使用中文保护标记包裹原始值 - String protectedText = PROTECT_START + original + PROTECT_END; - matcher.appendReplacement(sb, Matcher.quoteReplacement(protectedText)); - matcher.appendTail(sb); + original = matcher.group(); } - }else if(special){ + Matcher matcher2 = UNIT_CHN_REGEX.matcher(text); + if(matcher2.find()) { + original2= matcher2.group(); + } + int originalINdex= text.indexOf(original); + int originalINdex2= text.indexOf(original2); + if(originalINdex>0&&originalINdex2>0){ + if(originalINdex entry : PROTECT_PATTERNS.entrySet()) { Matcher matcher = entry.getValue().matcher(text); while (matcher.find()) { @@ -148,10 +170,10 @@ public class JiebaUtils { String protectedText = PROTECT_START + original + PROTECT_END; matcher.appendReplacement(sb, Matcher.quoteReplacement(protectedText)); matcher.appendTail(sb); - break; + break stop; } if (sb.length() > 0) { - break; + break stop; } } } @@ -254,7 +276,7 @@ public class JiebaUtils { JiebaSegmenter segmenter = new JiebaSegmenter(); loadUserDict(); String protectedText = protectPatterns(text); - System.out.println("protectedText: " + protectedText); + log.info("protectedText: {}", protectedText); List tokens = segmenter.sentenceProcess(protectedText).stream() //.filter(word -> word.length() > 1) // 过滤单字 // .sorted(Comparator.reverseOrder()) // 按词典词频降序 @@ -285,7 +307,13 @@ public class JiebaUtils { } else if (CATEGORY_LIBRARY.contains(word)) { fields.putIfAbsent("category", word); } else if (startsWithDigit(word)) { - fields.putIfAbsent("specs", word.toLowerCase()); + for (String unit:UNITS){ + if (word.contains(unit)) { + fields.putIfAbsent("specs", word); + break; + } + } + //fields.putIfAbsent("specs", word.toLowerCase()); }else if(SPECIAL_NAME.contains(word)){ fields.putIfAbsent("productShortName", word); }else { @@ -311,12 +339,12 @@ public class JiebaUtils { * */ if(words.size()>1){ - int index=words.size()-1; - if(!startsWithLetterOrDigitRegex(words.get(index))){ - fields.putIfAbsent("productShortName", words.get(index)); - }else { - fields.putIfAbsent("productShortName", words.get(index-1)); - } + // int index=words.size()-1; + // if(!startsWithLetterOrDigitRegex(words.get(index))){ + fields.putIfAbsent("productShortName", machProductName(words,fields.get("brand"))); +// }else { +// fields.putIfAbsent("productShortName", words.get(index-1)); +// } }else { fields.putIfAbsent("productShortName", words.get(0)); } @@ -324,6 +352,31 @@ public class JiebaUtils { return fields; } + /** + * 倒叙匹查询配商品名称 + * @param words + * @return + */ + private static String machProductName(List words,String brand){ + int index=words.size()-1; + String productName=words.get(index); + while (index>-1){ + productName = words.get(index); + if(productName.length()<2){ + index--; + continue; + } + if(startsWithLetterOrDigitRegex(productName)||containsFilterWord(productName)||containsNumberLoop(productName) + ||(StringUtils.isNotEmpty(brand)&&productName.contains(brand))){ + index--; + }else { + break; + } + } + return productName; + } + + /** * 特色字段匹配 * @return @@ -396,18 +449,50 @@ public class JiebaUtils { return text; } + /** + * 判断是否包含数字 + * @param input + * @return + */ + public static boolean containsNumberLoop(String input) { + for (char c : input.toCharArray()) { + if (Character.isDigit(c)) { + return true; + } + } + return false; + } + + /** + * 判断过滤词 + * @param input + * @return + */ + public static boolean containsFilterWord(String input) { + for (String word : filterWords) { + if ("\\*".equals(word)) { + if (input.contains("*")) return true; + } + if (input.contains(word)) { + return true; + } + } + return false; + } + + public static void main(String[] args) { // JiebaUtils jiebaUtils = new JiebaUtils(); - String text = "云计算和区块链是热门技术"; + String text = "四海油彩米10Kg"; // String text = "新鲜牛肉饺子500g"; //String text = "志高1.8L电热水壶"; // String text = "(单充)数据线2m"; // String text = "雅安利2.0*2.3四件套"; // String text = "RSCW-1949剃须刀"; - System.out.println(cleanNumberAndDigit("日本豆腐3条")); + // System.out.println(cleanNumberAndDigit("双汇王中王火腿肠400G10*40g/包")); //String text="六指鼠童袜001"; - List words = JiebaUtils.extractKeywords(text); - System.out.println(words); + // List words = JiebaUtils.extractKeywords(text); + //System.out.println(words); Map shopMap= JiebaUtils.getShopDetails(ProductTitleUtil.cleanTitle2(text)); System.out.println(shopMap); diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/ProductTitleUtil.java b/mall-common/src/main/java/com/suisung/mall/common/utils/ProductTitleUtil.java index 397e5181..9abde45e 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/ProductTitleUtil.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/ProductTitleUtil.java @@ -53,14 +53,17 @@ public class ProductTitleUtil { Arrays.asList("华为", "苹果", "小米", "三星", "美的", "格力", "耐克", "阿迪达斯", "海尔","雀巢","伊利","蒙牛","达能","乐事","多力多滋","三只松鼠","良品铺子","可口可乐", "农夫山泉","元气森林","红牛","雅诗兰黛","欧莱雅","玉兰油","科颜氏","宝洁","汰渍","帮宝适","联合利华","Unilever","含多芬","清扬","大窑","谢村桥牌阡糯","谢村桥牌阡", "六个核桃","大豫竹","优乐多","安慕希","纳爱斯","舒客","宜轩", "蓝月亮","海尔","美的","松下","戴森","耐克","安踏","李宁","特仑苏","纯甄","安井","三全","哇哈哈", - "龙江家园","达利园","春光","妙芙","南星","利嘉旺","卡得福","泓一","爱乡亲","思念","得力","中雪","江南点心局","德庄","六指鼠", - "依水塬","乌苏啤酒","阿尔卑斯", "瑞旗","振雷","中狗","宝视达","冷酸灵","骆驼","NIKE","PAMU","康师傅","信智利","双兔","安足莱","新博美","新博","创利") + "龙江家园","达利园","春光","妙芙","南星","利嘉旺","卡得福","泓一","爱乡亲","思念","得力","中雪","江南点心局","德庄","六指鼠","娃哈哈","开古","不二家","湘亮牌", + "依水塬","乌苏啤酒","阿尔卑斯", "瑞旗","振雷","中狗","宝视达","冷酸灵","骆驼","NIKE","PAMU","康师傅","信智利","双兔","安足莱","新博美","湘亮牌","忆江南","张骞牌", + "新博","创利","哈奇利","好口福","银鹭","开古","百事可乐","邛池","天旭牌","泗泉山","银狼","象芽王","拜将坛","健民","湘亮牌","周大黑","盛华牌","盛华","碗碗香","龙凤王", + "陕南健源","百州红","八度名苑","旭美","今麦郞","勇闯天涯","青岛","一起赢","元気森林","云南红茶","盈亮","树堂","乡糯香","汉中魏","金源","疆丝麦耘","四海油","亿家康", + "利民") )); /** * 品类词库(初始化后不可变) */ public static final Set CATEGORY_LIBRARY = Collections.unmodifiableSet(new HashSet<>( - Arrays.asList("手机", "电脑", "空调", "冰箱", "运动鞋", "T恤", "洗发水", "洗衣液","猪脚") + Arrays.asList("手机", "电脑", "空调", "冰箱", "运动鞋", "T恤", "洗发水", "洗衣液","猪脚","花生牛奶") )); static { @@ -88,7 +91,8 @@ public class ProductTitleUtil { //特殊商品 public static final Set SPECIAL_NAME = Collections.unmodifiableSet(new HashSet<>( - Arrays.asList("水饺", "面条", "包子","卷纸","卫生纸","紫菜汤","猪肉包","叉烧包","香菇青菜包","面包","黑米") + Arrays.asList("水饺", "面条", "包子","卷纸","卫生纸","紫菜汤","猪肉包","叉烧包","香菇青菜包","面包","黑米","棒棒糖","梳子","谢村花雕","古秦洋精","通裕梨花", + "台式烤香肠") )); static { @@ -104,8 +108,9 @@ public class ProductTitleUtil { Arrays.asList("个","条","份","盒","份","袋") )); public static final Set UNITS = Collections.unmodifiableSet(new HashSet<>( - Arrays.asList("g","克","kg","千克","ml","毫升","l","升","cm","厘米","mm","毫米","份","盒","份","袋") + Arrays.asList("g","克","kg","千克","ml","毫升","l","升","cm","厘米","mm","毫米","份","盒","份","袋","L") )); + private ProductTitleUtil() { } diff --git a/mall-common/src/main/resources/dict.txt b/mall-common/src/main/resources/dict.txt index b646fba3..872b7901 100644 --- a/mall-common/src/main/resources/dict.txt +++ b/mall-common/src/main/resources/dict.txt @@ -33,4 +33,106 @@ PAMU 500 n 叉烧包 500 n 香菇青菜包 500 n 老鸭汤炖料 500 n -六指鼠 500 n \ No newline at end of file +六指鼠 500 n +花生牛奶 500 n +碧螺春 500 n +红薯仔 500 n +棒棒糖 500 n +哈密瓜桃味 500 n +益生菌 500 n +茉莉花茶 500 n +棉花糖 500 n +照相机 500 n +涂鸦画 500 n +火腿肠 500 n +苏打水 500 n +薏米粥 500 n +好口福 500 n +面条 500 n +红豆花生奶 500 n +绣花被 500 n +黄山小毛峰 500 n +宜兴毛尖 500 n +龙井八宝茶 500 n +忆江南 500 n +山楂知己茶 500 n +枸杞茶 500 n +菊花茶 500 n +铁观音 500 n +汉中绿茶 500 n +汉中炒青 500 n +西乡毛尖 500 n +西乡特炒 500 n +西乡炒青 500 n +汉中特炒 500 n +汉中仙毫 500 n +百事可乐 500 n +胚芽茶 500 n +黑苦荞 500 n +茉莉银毫 500 n +茉莉花茶 500 n +茉莉毛尖 500 n +苦荞茶 500 n +云雾绿茶 500 n +黄山毛尖 500 n +汉中仙毫 500 n +拜将坛 500 n +汉中功夫红茶 500 n +汉中毛尖 500 n +云雾毛尖茶 500 n +信阳毛尖 500 n +雨前毛尖茶 500 n +雨前毛尖 500 n +速溶饮料 500 n +水蜜桃味 500 n +枣味茶 500 n +果味茶 500 n +黑米茶青柑茶 500 n +毛尖茶 500 n +冻干柠檬 500 n +汉中仙毫 500 n +云南滇红 500 n +决明子 500 n +清热下火茶 500 n +柠檬干 500 n +汉中大米 500 n +面皮粉 500 n +压榨油 500 n +百州红 500 n +八度名苑 500 n +苦荞 500 n +牛肉卷 500 n +迷尔山楂 500 n +九制梅肉 500 n +白馒头 500 n +山楂片茶 500 n +汉中红茶 500 n +电热锅 500 n +玫瑰花茶 500 n +冰糖雪梨 500 n +今麦郞 500 n +乌龙茶 500 n +雪花啤酒 500 n +勇闯天涯 500 n +谢村花雕 500 n +古秦洋酒 500 n +汉斯啤酒 500 n +北京二锅头 500 n +一起赢 500 n +古秦洋精 500 n +桃花浪漫 500 n +台式烤香肠 500 n +孜然散 500 n +香甜王 500 n +元気森林 500 n +苏打气泡水 500 n +云南红茶 500 n +碗碗香 500 n +丝苗香米 500 n +压榨菜籽油 500 n +乡糯香 500 n +鸡蛋面片 500 n +疆丝麦耘 500 n +亿家康 500 n +汉中香米 500 n +菜籽油 500 n \ No newline at end of file diff --git a/mall-search/src/main/java/com/suisung/mall/search/service/impl/EsProductImageServiceImpl.java b/mall-search/src/main/java/com/suisung/mall/search/service/impl/EsProductImageServiceImpl.java index 3903d277..fb227eb8 100644 --- a/mall-search/src/main/java/com/suisung/mall/search/service/impl/EsProductImageServiceImpl.java +++ b/mall-search/src/main/java/com/suisung/mall/search/service/impl/EsProductImageServiceImpl.java @@ -284,13 +284,16 @@ public class EsProductImageServiceImpl implements EsProductImageService { // 2. 为每个关键词构建查询 for (String keyword : keywords) { - String cleanKeyword = ProductTitleUtil.cleanTitle2(keyword); + //keyword=keyword.split(" ")[0]; + String cleanKeyword= ProductTitleUtil.cleanTitle2(keyword); + // String[] cleanKeywords = cleanWord.split(" "); + // String cleanKeyword=cleanKeywords[0]; keywordToCleanMap.put(cleanKeyword, keyword); // 保留原始关键词映射 // 构建组合查询 BoolQueryBuilder boolQuery = QueryBuilders.boolQuery() //.should(QueryBuilders.matchQuery("barcode", cleanKeyword).boost(1.0f)) - .should(QueryBuilders.matchQuery("productName", JiebaUtils.cleanNumberAndDigit(cleanKeyword)).boost(1.0f)) + .should(QueryBuilders.matchQuery("productName", JiebaUtils.cleanNumberAndDigit(keyword)).boost(1.0f)) .should(QueryBuilders.fuzzyQuery("cleanName.keyword", JiebaUtils.cleanNumberAndDigit(cleanKeyword)) .fuzziness(Fuzziness.ONE) .maxExpansions(20) @@ -314,7 +317,6 @@ public class EsProductImageServiceImpl implements EsProductImageService { .boost(0.5f)); count+=1; } - if(StringUtil.isNotEmpty(shopDetailMap.get("specs"))){ boolQuery.should(QueryBuilders.fuzzyQuery("specs.keyword", shopDetailMap.get("specs")) // 模糊查询权重 .fuzziness(Fuzziness.TWO) // 自动确定模糊度 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/number/service/ShopNumberSeqService.java b/mall-shop/src/main/java/com/suisung/mall/shop/number/service/ShopNumberSeqService.java index b2234a1a..56cc0ac8 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/number/service/ShopNumberSeqService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/number/service/ShopNumberSeqService.java @@ -33,4 +33,8 @@ public interface ShopNumberSeqService extends IBaseService { List getBatchUserAccountBaseId(int batchSize); void clearKeyStoreAccountBaseId(); + + List getBatchSpecId(int batchSize); + + void clearKeyStoreSepcId(); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/number/service/impl/ShopNumberSeqServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/number/service/impl/ShopNumberSeqServiceImpl.java index 501c0492..216820a9 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/number/service/impl/ShopNumberSeqServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/number/service/impl/ShopNumberSeqServiceImpl.java @@ -3,10 +3,12 @@ package com.suisung.mall.shop.number.service.impl; import cn.hutool.core.date.DateUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.suisung.mall.common.feignService.AccountService; +import com.suisung.mall.common.modules.base.ShopBaseProductSpec; import com.suisung.mall.common.modules.number.ShopNumberSeq; import com.suisung.mall.common.modules.product.ShopProductSpecItem; import com.suisung.mall.core.web.service.RedisService; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; +import com.suisung.mall.shop.base.service.ShopBaseProductSpecService; import com.suisung.mall.shop.number.mapper.ShopNumberSeqMapper; import com.suisung.mall.shop.number.service.ShopNumberSeqService; import com.suisung.mall.shop.product.service.ShopProductSpecItemService; @@ -45,6 +47,9 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl getBatchSpecId(int batchSize) { + int start=0; + if(null!=redisService.get(RedisKey.STOREDATASPECID)){ + start=(Integer) redisService.get(RedisKey.STOREDATASPECID); + redisService.set(RedisKey.STOREDATASPECID,start+batchSize); + return IntStream.rangeClosed(start+1, start+batchSize).boxed().collect(Collectors.toList()); + } + QueryWrapper queryWrapper= new QueryWrapper<>(); + queryWrapper.select("max(spec_id) as spec_id"); + ShopBaseProductSpec shopBaseProductSpec=shopBaseProductSpecService.getOne(queryWrapper); + if(null!=shopBaseProductSpec){ + start=shopBaseProductSpec.getSpec_id(); + redisService.set(RedisKey.STOREDATASPECID,start+batchSize); + } + if(start==0){ + redisService.set(RedisKey.STOREDATASPECID,start+batchSize); + return IntStream.rangeClosed(start+1, start+batchSize).boxed().collect(Collectors.toList()); + } + return IntStream.rangeClosed(start+1, start+batchSize).boxed().collect(Collectors.toList()); + } + /** * 存的是最大值,取的是范围,如存最大值1,批量是2,则取范围【1+1,1+2】,如果没有则从1开始算即取范围[0+1,0+2] * @param batchSize @@ -272,6 +306,7 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl data = new HashMap<>(); - UserDto user = getCurrentUser(); + UserDto user = ContextUtil.getCurrentUser(); //读取模板 Map data_arr = null; @@ -198,7 +199,7 @@ public class ShopPageAppController extends BaseControllerImpl { @RequestParam(name = "app_page_list") String app_page_list, @RequestParam(name = "page_config", defaultValue = "{}") String page_config, @RequestParam(name = "app_member_center", defaultValue = "{}") String app_member_center) { - UserDto user = getCurrentUser(); + UserDto user = ContextUtil.getCurrentUser(); if (user == null) { throw new ApiUserException(I18nUtil._("用户信息异常!")); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/page/controller/admin/ShopPageBaseController.java b/mall-shop/src/main/java/com/suisung/mall/shop/page/controller/admin/ShopPageBaseController.java index d7126798..06f778d8 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/page/controller/admin/ShopPageBaseController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/page/controller/admin/ShopPageBaseController.java @@ -16,6 +16,7 @@ import com.suisung.mall.common.modules.page.ShopPageBase; import com.suisung.mall.common.modules.product.ShopPageUserForm; import com.suisung.mall.common.service.impl.BaseControllerImpl; import com.suisung.mall.common.utils.CheckUtil; +import com.suisung.mall.common.utils.ContextUtil; import com.suisung.mall.common.utils.I18nUtil; import com.suisung.mall.core.web.CommonRes; import com.suisung.mall.shop.page.dto.PageBaseListReq; @@ -151,7 +152,7 @@ public class ShopPageBaseController extends BaseControllerImpl { @RequestMapping(value = "/remove", method = RequestMethod.POST) public CommonResult remove(@RequestParam(name = "page_id") Long page_id) { try { - UserDto user = getCurrentUser(); + UserDto user = ContextUtil.getCurrentUser(); // 用户未登录,禁止操作 if (user == null) { @@ -250,7 +251,7 @@ public class ShopPageBaseController extends BaseControllerImpl { */ - UserDto user = getCurrentUser(); + UserDto user = ContextUtil.getCurrentUser(); if (user == null) { throw new ApiUserException(I18nUtil._("用户信息异常!")); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/BrandModelExcel.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/BrandModelExcel.java index c25a02ae..917c9cb8 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/BrandModelExcel.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/BrandModelExcel.java @@ -10,7 +10,7 @@ import lombok.Data; public class BrandModelExcel{ public static final String TEMPLATE_NAME = "品牌导入模板.xlsx"; @ApiModelProperty("品牌名称") - @ExcelProperty(value = "商品名称", index = 0) + @ExcelProperty(value = "品牌名称", index = 0) private String brand_name; @ApiModelProperty("品牌描述") diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/SxGoosModelExcel.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/SxGoosModelExcel.java index 025bf542..e6ecf057 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/SxGoosModelExcel.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/exelModel/SxGoosModelExcel.java @@ -62,5 +62,12 @@ public class SxGoosModelExcel { @ExcelProperty(value = "品牌名称", index = 8) private String brand_name; + @ApiModelProperty(value = "多规格参数,[规格名称:规格值]",example = "[颜色:白色][尺码:28][价格:]") + @ExcelProperty(value = "多规格参数", index = 9) + private String shop_specs; + + @ExcelProperty(value = "多规格json数据", index = 9) + @ExcelIgnore + private String jsonSpecs; } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/ProductSpecManager.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/ProductSpecManager.java new file mode 100644 index 00000000..d2c9daee --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/ProductSpecManager.java @@ -0,0 +1,188 @@ +package com.suisung.mall.shop.sync.keymanage; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.math.BigDecimal; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ProductSpecManager { + // 定义商品规格项 + public static class SpecItem { + private String name; + private String value; + + public SpecItem() {} + + public SpecItem(String name, String value) { + this.name = name; + this.value = value; + } + + // Getters and Setters + public String getName() { return name; } + public void setName(String name) { this.name = name; } + public String getValue() { return value; } + public void setValue(String value) { this.value = value; } + + @Override + public String toString() { + return name + ": " + value; + } + } + + // 定义价格库存项 + public static class PriceStockItem { + private BigDecimal price; + private BigDecimal stock; + + public PriceStockItem() {} + + public PriceStockItem(BigDecimal price, BigDecimal stock) { + this.price = price; + this.stock = stock; + } + + // Getters and Setters + public BigDecimal getPrice() { return price; } + public void setPrice(BigDecimal price) { this.price = price; } + public BigDecimal getStock() { return stock; } + public void setStock(BigDecimal stock) { this.stock = stock; } + + @Override + public String toString() { + return String.format("¥%.2f (%d件)", price, stock); + } + } + + // 定义完整的商品规格信息 + public static class ProductSpecInfo { + private List spec; + private PriceStockItem priceAndstock; + + public ProductSpecInfo() { + this.spec = new ArrayList<>(); + } + + // Getters and Setters + public List getSpec() { return spec; } + public void setSpec(List spec) { this.spec = spec; } + public PriceStockItem getPriceAndstock() { return priceAndstock; } + public void setPriceAndstock(PriceStockItem priceAndstock) { + this.priceAndstock = priceAndstock; + } + + } + + // 商品规格映射表:商品ID -> JSON字符串 + private Map> specsMap; + private static Gson gson = new GsonBuilder().setPrettyPrinting().create(); + + public Map> getSpecsMap() { + return specsMap; + } + + public void setSpecsMap(Map> specsMap) { + this.specsMap = specsMap; + } + + /** + * 添加商品规格 + * + * @param productId 商品ID + * @param specItems 规格项列表 + * @param price 价格 + * @param stock 库存 + */ + public void addProductSpec(String productId, List specItems, + BigDecimal price, BigDecimal stock) { + // 创建价格库存项 + PriceStockItem priceStockItem = new PriceStockItem(price, stock); + + // 创建商品规格信息 + ProductSpecInfo specInfo = new ProductSpecInfo(); + specInfo.setSpec(specItems); + specInfo.setPriceAndstock(priceStockItem); + + // 转换为JSON字符串 + String json = gson.toJson(specInfo); + + // 添加到Map + if (!specsMap.containsKey(productId)) { + specsMap.put(productId, new ArrayList<>()); + } + specsMap.get(productId).add(json); + } + + /** + * 获取商品所有规格信息 + * + * @param productId 商品ID + * @return 商品规格信息列表 + */ + public String getProductSpecsString(String productId) { + if (!specsMap.containsKey(productId)) { + return ""; + } + return gson.toJson(specsMap.get(productId)); + } + + /** + * 获取商品所有规格信息 + * + * @param productId 商品ID + * @return 商品规格信息列表 + */ + public List getSepcMapValue(String productId) { + if (!specsMap.containsKey(productId)) { + return Collections.emptyList(); + } + return specsMap.get(productId); + } + + /** + * 获取商品所有规格信息 + * + * @param productId 商品ID + * @return 商品规格信息列表 + */ + public List getProductSpecs(String productId) { + if (!specsMap.containsKey(productId)) { + return Collections.emptyList(); + } + + List result = new ArrayList<>(); + for (String json : specsMap.get(productId)) { + ProductSpecInfo info = gson.fromJson(json, ProductSpecInfo.class); + result.add(info); + } + return result; + } + + /** + * 解析规格字符串为SpecItem列表 + * + * @param specStr 规格字符串,格式如"[颜色:黑色][尺寸:30]" + * @return SpecItem列表 + */ + public List parseSpecString(String specStr) { + List specItems = new ArrayList<>(); + + // 正则表达式匹配格式:[key:value] + Pattern pattern = Pattern.compile("\\[(.*?):(.*?)\\]"); + Matcher matcher = pattern.matcher(specStr); + + while (matcher.find()) { + if (matcher.groupCount() == 2) { + String name = matcher.group(1).trim(); + String value = matcher.group(2).trim(); + specItems.add(new SpecItem(name, value)); + } + } + + return specItems; + } + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/RedisKey.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/RedisKey.java index 0234a7d9..d89a5068 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/RedisKey.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/RedisKey.java @@ -20,4 +20,7 @@ public class RedisKey { public static final String STOREDATACCOUNTBASEID="storedata:accountBaseId"; + + + public static final String STOREDATASPECID="storedata:SpecId"; } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/listen/ShopBatchSubmitListener.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/listen/ShopBatchSubmitListener.java index ea5ce4db..6bfa892a 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/listen/ShopBatchSubmitListener.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/listen/ShopBatchSubmitListener.java @@ -4,10 +4,15 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.json.JSONArray; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.google.gson.Gson; +import com.suisung.mall.common.enums.DicEnum; +import com.suisung.mall.common.modules.sync.SyncStoreSpecs; import com.suisung.mall.common.utils.CommonUtil; import com.suisung.mall.common.utils.StringUtils; import com.suisung.mall.shop.sync.exelModel.SxGoosModelExcel; +import com.suisung.mall.shop.sync.keymanage.ProductSpecManager; +import com.suisung.mall.shop.sync.service.SyncStoreSpecsService; import com.suisung.mall.shop.sync.service.SyncThirdDataService; import lombok.Getter; import lombok.Setter; @@ -15,13 +20,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; +import java.util.*; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -30,18 +30,23 @@ import java.util.stream.Collectors; public class ShopBatchSubmitListener extends AnalysisEventListener { // 批处理阈值 private static final int BATCH_SIZE = 500; + // 数据缓存 + private List cachedDataList = new ArrayList<>(BATCH_SIZE); + + private SyncThirdDataService syncThirdDataService; + + private SyncStoreSpecsService syncStoreSpecsService; + // 线程池配置 private final ExecutorService executorService; - // 数据缓存 - private final List cachedDataList = new ArrayList<>(BATCH_SIZE); - private final SyncThirdDataService syncThirdDataService; - private final List> futures; - private final AtomicInteger success; - private final AtomicInteger fails; - private final AtomicInteger batchSize; + + private List> futures ; + private AtomicInteger success; + private AtomicInteger fails; + private AtomicInteger batchSize; @Setter @Getter - private String storeId; + private String storeId; @Setter @Getter @@ -49,19 +54,24 @@ public class ShopBatchSubmitListener extends AnalysisEventListener brandMaps; + private Map brandMaps; + private ProductSpecManager productSpecManager; - public ShopBatchSubmitListener(SyncThirdDataService syncThirdDataService) { + public ShopBatchSubmitListener(SyncThirdDataService syncThirdDataService,SyncStoreSpecsService syncStoreSpecsService) { this.syncThirdDataService = syncThirdDataService; + this.syncStoreSpecsService = syncStoreSpecsService; // 创建线程池(根据CPU核心数优化) int corePoolSize = Runtime.getRuntime().availableProcessors(); - // log.info("核心线程数量{}", corePoolSize); - this.executorService = Executors.newFixedThreadPool(corePoolSize); + log.info("核心线程数量{}" , corePoolSize); + this.executorService = Executors.newFixedThreadPool(6); this.futures = new ArrayList<>(); this.success = new AtomicInteger(); this.fails = new AtomicInteger(); - this.batchSize = new AtomicInteger(); + this.batchSize= new AtomicInteger(); + this.productSpecManager=new ProductSpecManager(); + Map> specsMap=new HashMap<>(); + productSpecManager.setSpecsMap(specsMap); } @Override @@ -74,6 +84,7 @@ public class ShopBatchSubmitListener extends AnalysisEventListener future : futures) { try { - log.info("任务结果:{}", future.get()); + log.info("任务结果:{}" ,future.get()); } catch (Exception e) { log.info("任务执行异常: {}", e.getMessage()); } } - log.info("Excel解析完成,总处理条数: {}", context.readSheetHolder().getTotal()); - log.info("成功数量:{};失败数量:{}", success.get(), fails.get()); + log.info("Excel解析完成,总处理条数: {}" , context.readSheetHolder().getTotal()); + log.info("成功数量:{};失败数量:{}",success.get(),fails.get()); // 关闭线程池 executorService.shutdown(); } @@ -105,26 +117,28 @@ public class ShopBatchSubmitListener extends AnalysisEventListener batchCopy = new ArrayList<>(deduplicateById(cachedDataList)); - log.info("去重前:{};去重后:{}", cachedDataList.size(), batchCopy.size()); + Map> sepcsMap = new HashMap<>(productSpecManager.getSpecsMap()); + log.info("去重前:{};去重后:{}" , cachedDataList.size(), batchCopy.size()); final int index = batchSize.get(); - futures.add(executorService.submit(() -> { - int i = 0; - while (true) { + futures.add(executorService.submit(()->{ + int i=0; + while (true){ i++; try { - Gson gson = new Gson(); - String jsonShops = gson.toJson(batchCopy); - JSONArray jsonArray = new JSONArray(jsonShops); - syncThirdDataService.baseSaveOrUpdateGoodsBatch(jsonArray, storeId, isNegativeAllowed, brandMaps); + Gson gson=new Gson(); + String jsonShops=gson.toJson(batchCopy); + JSONArray jsonArray=new JSONArray(jsonShops); + syncThirdDataService.baseSaveOrUpdateGoodsBatch(jsonArray,storeId,isNegativeAllowed,brandMaps); log.info("已提交批次: {} 条", batchCopy.size()); success.getAndIncrement(); - return "完成批次:" + index; + saveSnycStoreSpec(sepcsMap); + return "完成批次:"+index; } catch (Exception e) { - if (i < 2) { + if(i<2){ continue; } fails.getAndIncrement(); - return "失败批次:" + index + ";失败原因:" + e.getMessage(); + return "失败批次:"+index+";失败原因:"+e.getMessage(); } } })); @@ -132,34 +146,144 @@ public class ShopBatchSubmitListener extends AnalysisEventListener deduplicateById(List list) { return list.stream() .peek(sxGoosModelExcel -> { - if (StringUtils.isNotEmpty(sxGoosModelExcel.getShop_spec())) { + if(StringUtils.isNotEmpty(sxGoosModelExcel.getShop_spec())){ sxGoosModelExcel.setProduct_spec(Collections.singletonList(sxGoosModelExcel.getShop_spec())); } - if (null == sxGoosModelExcel.getUnit()) { + if(null==sxGoosModelExcel.getUnit()){ sxGoosModelExcel.setUnit(""); } - if (ObjectUtil.isEmpty(sxGoosModelExcel.getBuy_limit())) { + if(ObjectUtil.isEmpty(sxGoosModelExcel.getBuy_limit())){ sxGoosModelExcel.setBuy_limit(0); } - if (StringUtils.isEmpty(sxGoosModelExcel.getBrand_name())) { + if(StringUtils.isEmpty(sxGoosModelExcel.getBrand_name())){ sxGoosModelExcel.setBrand_name("其它品牌"); } - if (ObjectUtil.isEmpty(sxGoosModelExcel.getStock())) { + if(ObjectUtil.isEmpty(sxGoosModelExcel.getStock())){ sxGoosModelExcel.setStock(BigDecimal.ZERO); } sxGoosModelExcel.setProduct_number(sxGoosModelExcel.getProduct_barcode()); sxGoosModelExcel.setOriginal_price(sxGoosModelExcel.getRetail_price()); + + if(StringUtils.isNotEmpty(sxGoosModelExcel.getShop_specs())){ + List specItems= productSpecManager.parseSpecString(sxGoosModelExcel.getShop_specs()); + sxGoosModelExcel.setJsonSpecs(specItems.toString()); + productSpecManager.addProductSpec(sxGoosModelExcel.getProduct_barcode(),specItems,sxGoosModelExcel.getRetail_price(),sxGoosModelExcel.getStock()); + } }) .filter(CommonUtil.distinctByKey(SxGoosModelExcel::getProduct_number)) .collect(Collectors.toList()); } + /** + * 保存多规格数据 + */ + private void saveSnycStoreSpec(Map> listMap){ + if(listMap==null){ + return; + } + //List syncStoreSpecs=new ArrayList<>(); + List addSyncStoreSpecs=new ArrayList<>(); + List updateSyncStoreSpecs=new ArrayList<>(); + Map existId= getExistProductId(listMap); + if(existId==null){ + return; + } + listMap.forEach((k,v)->{ + SyncStoreSpecs syncStoreSpec=new SyncStoreSpecs(); + syncStoreSpec.setProductNumber(k); + syncStoreSpec.setJsonSpecs(v.toString()); + syncStoreSpec.setIsDeal(DicEnum.YESORNO_0.getCode()); + syncStoreSpec.setStoreId(Integer.valueOf(storeId)); + if(existId.containsKey(k)){ + syncStoreSpec.setSycnSpecsId(existId.get(k)); + updateSyncStoreSpecs.add(syncStoreSpec); + }else { + addSyncStoreSpecs.add(syncStoreSpec); + } + }); + if(!addSyncStoreSpecs.isEmpty()){ + syncStoreSpecsService.saveBatch(addSyncStoreSpecs,addSyncStoreSpecs.size()); + } + if(!updateSyncStoreSpecs.isEmpty()){ + syncStoreSpecsService.updateBatchById(updateSyncStoreSpecs,updateSyncStoreSpecs.size()); + } + + } + + /** + * 查找存量数据 + * @param listMap + * @return + */ + private Map getExistProductId(Map> listMap){ + QueryWrapper queryWrapper = new QueryWrapper<>(); + listMap.forEach((k,v)->{ + queryWrapper.or(q->q.eq("store_id",storeId).eq("product_number",k)); + }); + + List syncStoreSpecs = syncStoreSpecsService.list(queryWrapper); + if(syncStoreSpecs==null){ + return null; + } + Map resultMap=new HashMap<>(); + syncStoreSpecs.forEach(m->{ + resultMap.put(String.valueOf(m.getProductNumber()),m.getSycnSpecsId()); + }); + return resultMap; + } + + public static void main(String[] args) { + SxGoosModelExcel sxGoosModelExcel = new SxGoosModelExcel(); + sxGoosModelExcel.setProduct_barcode("1"); + sxGoosModelExcel.setProduct_name("产品1"); + sxGoosModelExcel.setShop_specs("颜色"); + sxGoosModelExcel.setRetail_price(new BigDecimal("100")); + sxGoosModelExcel.setStock(new BigDecimal("10")); + sxGoosModelExcel.setJsonSpecs("[颜色:红色][尺寸:10]"); + + SxGoosModelExcel sxGoosModelExcel1 = new SxGoosModelExcel(); + sxGoosModelExcel1.setProduct_barcode("2"); + sxGoosModelExcel1.setProduct_name("产品2"); + sxGoosModelExcel1.setShop_specs("颜色"); + sxGoosModelExcel1.setShop_spec("红色"); + sxGoosModelExcel1.setRetail_price(new BigDecimal("200")); + sxGoosModelExcel1.setStock(new BigDecimal("20")); + sxGoosModelExcel1.setJsonSpecs("[颜色:黑色][尺寸:20]"); + + SxGoosModelExcel sxGoosModelExcel3 = new SxGoosModelExcel(); + sxGoosModelExcel3.setProduct_barcode("1"); + sxGoosModelExcel3.setProduct_name("产品3"); + sxGoosModelExcel3.setShop_specs("颜色"); + sxGoosModelExcel3.setShop_spec("黑色"); + sxGoosModelExcel3.setRetail_price(new BigDecimal("300")); + sxGoosModelExcel3.setStock(new BigDecimal("30")); + sxGoosModelExcel3.setJsonSpecs("[颜色:黑色][尺寸:30]"); + + List sxGoosModelExcelList = new ArrayList<>(); + sxGoosModelExcelList.add(sxGoosModelExcel); + sxGoosModelExcelList.add(sxGoosModelExcel1); + sxGoosModelExcelList.add(sxGoosModelExcel3); + ProductSpecManager productSpecManager=new ProductSpecManager(); + Map> specsMap=new HashMap<>(); + productSpecManager.setSpecsMap(specsMap); + + sxGoosModelExcelList=sxGoosModelExcelList.stream().peek(gm -> { + if(StringUtils.isNotEmpty(gm.getJsonSpecs())){ + List specItems= productSpecManager.parseSpecString(gm.getJsonSpecs()); + productSpecManager.addProductSpec(gm.getProduct_barcode(),specItems,gm.getRetail_price(),gm.getStock()); + } + }).filter(CommonUtil.distinctByKey(SxGoosModelExcel::getProduct_barcode)) + .collect(Collectors.toList()); + Gson goson=new Gson(); + log.info("规格:{}",productSpecManager.getSepcMapValue("1")); + log.info("规格数量:{}",productSpecManager.getSepcMapValue("1").size()); + log.info(String.valueOf(sxGoosModelExcelList.size())); + } } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/SyncStoreSpecsMapper.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/SyncStoreSpecsMapper.java new file mode 100644 index 00000000..4201884d --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/SyncStoreSpecsMapper.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.sync.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.suisung.mall.common.modules.sync.SyncStoreSpecs; +import feign.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface SyncStoreSpecsMapper extends BaseMapper { + + List findSyncStoreSpecsList(SyncStoreSpecs syncStoreSpecs,@Param("limit") Integer limit,@Param("pageSize") Integer pageSize); +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncStoreSpecsService.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncStoreSpecsService.java new file mode 100644 index 00000000..d3ad0411 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncStoreSpecsService.java @@ -0,0 +1,9 @@ +package com.suisung.mall.shop.sync.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.suisung.mall.common.modules.sync.SyncStoreSpecs; + +public interface SyncStoreSpecsService extends IService { + + void dealSyncStoreSpecs(Integer storeId); +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/ProductMappingServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/ProductMappingServiceImpl.java index 9124d8a1..4d172838 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/ProductMappingServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/ProductMappingServiceImpl.java @@ -130,7 +130,7 @@ public class ProductMappingServiceImpl extends BaseServiceImpl shopProductBaseList,Integer storeId,Map shopProductSpecItemMap,Map ShopBaseProductSpecMap, Map productMappingMap,boolean isUpdate,String isPubish) { - shopProductBaseList= shopProductBaseList.stream().filter(base ->StateCode.PRODUCT_STATE_OFF_THE_SHELF_UNCHECK==(base.getProduct_state_id())).collect(Collectors.toList()); + shopProductBaseList= shopProductBaseList.stream().filter(base ->ObjectUtil.isNotEmpty(base.getUnit_name())).collect(Collectors.toList()); if (CollUtil.isEmpty(shopProductBaseList)) { log.info("没有规格数据要处理"); return; @@ -266,6 +266,7 @@ public class ProductMappingServiceImpl extends BaseServiceImpl implements SyncStoreSpecsService { + + private final Integer batchSize = 500; + @Autowired + private ShopBaseProductSpecService shopBaseProductSpecService; + @Autowired + private ShopProductSpecItemService shopProductSpecItemService; + @Autowired + private ShopNumberSeqService shopNumberSeqService; + @Autowired + private ShopBaseProductTypeService shopBaseProductTypeService; + + @Autowired + private ShopProductItemService shopProductItemService; + + @Autowired + private ShopProductItemSeqService shopProductItemSeqService; + + @Autowired + private ShopProductInfoService shopProductInfoService; + + @Autowired + private ShopProductIndexService shopProductIndexService; + + @Autowired + private SyncStoreSpecsMapper syncStoreSpecsMapper; + + + + @Autowired + private PlatformTransactionManager transactionManager; + + @Override + public void dealSyncStoreSpecs(Integer storeId) { + ExecutorService executor = Executors.newFixedThreadPool(6); + AtomicInteger success=new AtomicInteger(0); + AtomicInteger fails=new AtomicInteger(0); + shopNumberSeqService.clearKeyStoreSepcId(); + List> futures = new ArrayList<>(); + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("store_id", storeId); + wrapper.eq("is_deal", DicEnum.YESORNO_0.getCode()); + SyncStoreSpecs queryDto = new SyncStoreSpecs(); + queryDto.setStoreId(storeId); + queryDto.setIsDeal(DicEnum.YESORNO_0.getCode()); + long total= this.count(wrapper); + int pages= CommonUtil.getPagesCount(Math.toIntExact(total),batchSize); + for (int i=1;i<=pages;i++){ + int finalI = i; + futures.add(executor.submit(() -> { + try { + List syncStoreSpecsList= syncStoreSpecsMapper.findSyncStoreSpecsList(queryDto, finalI -1,batchSize); + //处理多规格数据 + dealShopItemSpec(syncStoreSpecsList,storeId); + success.getAndIncrement(); + syncStoreSpecsList.forEach(syncStoreSpecs -> { + syncStoreSpecs.setIsDeal(DicEnum.YESORNO_1.getCode()); + }); + this.updateBatchById(syncStoreSpecsList,syncStoreSpecsList.size()); + }catch (Exception e){ + log.error("失败原因:"+e.getMessage(),e); + fails.getAndIncrement(); + } + })); + } + // 等待所有任务完成 + for (Future future : futures) { + try { + System.out.println("规格任务结果: " + future.get()); + } catch (Exception e) { + System.err.println("规格任务执行异常: " + e.getMessage()); + } + } + executor.shutdown(); + shopNumberSeqService.clearKeyStoreSepcId(); + log.info("处理多规格数据成功:{}批,失败:{}批", success.get(), fails.get()); + } + + /** + * 处理多规格商品 + */ + @Transactional + public void dealShopItemSpec(List syncStoreSpecsList,Integer storeId){ + TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); + transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); + transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); + transactionTemplate.setTimeout(200); // 60秒超时 + transactionTemplate.execute(status -> { + try { + ProductSpecManager productSpecManager=new ProductSpecManager(); + Map> specMap=new HashMap<>();//存储规格类型 + Map> specValueMap=new HashMap<>();//存储规格的值 + Map specCateGoryMap=new HashMap<>();//存储分类id + Map specsProductIdMap=new HashMap<>();//存储product_id + //Map existProductItemMap=new HashMap<>();//存储item_id + Gson gson=new Gson(); + //规格定义 + List shopBaseProductSpecList=new ArrayList<>(); + //规格值定义 + //解析json + for (SyncStoreSpecs syncStoreSpecs : syncStoreSpecsList) { + List jsonSpec= new ArrayList<>(); + cn.hutool.json.JSONArray jsonArray=JSONUtil.parseArray(syncStoreSpecs.getJsonSpecs()); + for (Object obj : jsonArray) { + jsonSpec.add(obj.toString()); + } + specMap.put(String.valueOf(syncStoreSpecs.getProductNumber()),jsonSpec); + specCateGoryMap.put(String.valueOf(syncStoreSpecs.getProductNumber()),syncStoreSpecs.getCategoryId()); + specsProductIdMap.put(syncStoreSpecs.getProductNumber(),syncStoreSpecs.getProductId()); + // existProductItemMap.put(syncStoreSpecs.getProductNumber(),syncStoreSpecs.getItemId()); + } + productSpecManager.setSpecsMap(specMap); + List shopProductItems=new ArrayList<>(); + specMap.forEach((k,v)->{ + List specInfos= productSpecManager.getProductSpecs(k); + for (ProductSpecManager.ProductSpecInfo specInfo : specInfos) { + List specList=specInfo.getSpec(); + StringBuilder specValue=new StringBuilder(); + for (ProductSpecManager.SpecItem specItem : specList) { + ShopBaseProductSpec shopBaseProductSpec=new ShopBaseProductSpec(); + shopBaseProductSpec.setStore_id(storeId); + shopBaseProductSpec.setSpec_name(specItem.getName()); + shopBaseProductSpec.setSpec_format("text"); + shopBaseProductSpec.setSpec_order(50); + shopBaseProductSpec.setSpec_category_id(specCateGoryMap.get(k)); + shopBaseProductSpecList.add(shopBaseProductSpec); + List specValueList=new ArrayList<>(); + if(specValueMap.containsKey(specItem.getName())){ + specValueList=specValueMap.get(specItem.getName()); + } + if(StringUtils.isNotEmpty(specValue)){ + specValue.append(",").append(specItem.getValue()); + }else { + specValue.append(specItem.getValue()); + } + specValueList.add(specItem.getValue()); + specValueMap.put(specItem.getName(),specValueList); + } + ProductSpecManager.PriceStockItem stockItem= specInfo.getPriceAndstock(); + ShopProductItem spuItem=new ShopProductItem(); + spuItem.setItem_barcode(k); + spuItem.setItem_number(k); + spuItem.setItem_name(specValue.toString()); + spuItem.setItem_unit_price(stockItem.getPrice()); + spuItem.setItem_market_price(stockItem.getPrice()); + spuItem.setItem_quantity(stockItem.getStock().intValue()); + shopProductItems.add(spuItem); + } + }); + //1.新增规格 + //过滤 + List addShopBaseProductSpecList=new ArrayList<>(); + List updateShopBaseProductSpecList=new ArrayList<>(); + List shopBaseProductSpecs=shopBaseProductSpecList.stream() + .filter(com.suisung.mall.common.utils.CommonUtil.distinctByKey(ShopBaseProductSpec::getSpec_name)) + .collect(Collectors.toList()); + Map existsMap= getExistShopBaseSpec(shopBaseProductSpecs,storeId); + Map specCateGoryIdMap=new HashMap<>(); + Map> cateGoryIdSpecIdMap=new HashMap<>(); + for(ShopBaseProductSpec spec:shopBaseProductSpecs){ + if(existsMap.containsKey(spec.getSpec_name())){ + spec.setSpec_id(existsMap.get(spec.getSpec_name())); + specCateGoryIdMap.put(spec.getSpec_name(),spec.getSpec_id()); + updateShopBaseProductSpecList.add(spec); + }else { + addShopBaseProductSpecList.add(spec); + } + } + + if(!addShopBaseProductSpecList.isEmpty()){ + List specIds= shopNumberSeqService.getBatchSpecId(addShopBaseProductSpecList.size());//计算specId + for (int i=0;i specIdList=new ArrayList<>(); + if(cateGoryIdSpecIdMap.containsKey(addShopBaseProductSpecList.get(i).getSpec_category_id())){ + specIdList=cateGoryIdSpecIdMap.get(addShopBaseProductSpecList.get(i).getSpec_category_id()); + } + specIdList.add(specIds.get(i)); + cateGoryIdSpecIdMap.put(addShopBaseProductSpecList.get(i).getSpec_category_id(),specIdList); + specCateGoryIdMap.put(addShopBaseProductSpecList.get(i).getSpec_name(),specIds.get(i)); + } + //shopBaseProductSpecService.saveBatch(addShopBaseProductSpecList,addShopBaseProductSpecList.size()); + } +// if(!updateShopBaseProductSpecList.isEmpty()){ +// shopBaseProductSpecService.updateBatchById(updateShopBaseProductSpecList,updateShopBaseProductSpecList.size()); +// } + //2.新增规格的值 + List specItems=new ArrayList<>(); + specValueMap.forEach((k,v)->{ + for (String specVal : v) { + ShopProductSpecItem specItem = new ShopProductSpecItem(); + specItem.setSpec_id(specCateGoryIdMap.get(k)); + specItem.setCategory_id(specCateGoryMap.get(k)); + specItem.setStore_id(storeId); + specItem.setSpec_item_enable(1); + specItem.setSpec_item_name(specVal); + specItem.setSpec_item_order("10"); + specItem.setSpecName(k); + specItems.add(specItem); + } + }); + Map specItemMap=new HashMap<>();//存储specItemValue对应的ShopProductSpecItem + List specItemList=specItems.stream() + .filter(com.suisung.mall.common.utils.CommonUtil.distinctByKey(ShopProductSpecItem::getSpec_item_name)) + .collect(Collectors.toList());//去重 + List addSpecItemList=new ArrayList<>(); + List updateSpecItemList=new ArrayList<>(); + Map specItemIdMap=getExistShopSpecItem(specItemList,storeId); + for(ShopProductSpecItem specItem:specItemList){ + if(specItemIdMap.containsKey(specItem.getSpec_item_name())){ + specItem.setSpec_item_id(specItemIdMap.get(specItem.getSpec_item_name())); + updateSpecItemList.add(specItem); + }else { + addSpecItemList.add(specItem); + } + specItemMap.put(specItem.getSpec_item_name(),specItem); + } + + if(!addSpecItemList.isEmpty()){ + List specItemIds= shopNumberSeqService.getBatchSpecItemId(addSpecItemList.size());//计算specId + for (int i=0;i shopBaseProductTypes= getShopBaseProductTypeList(syncStoreSpecsList,storeId); + for(ShopBaseProductType shopBaseProductType:shopBaseProductTypes){ + List specIdList=new ArrayList<>(); + if(cateGoryIdSpecIdMap.containsKey(shopBaseProductType.getType_category_id())){ + specIdList=cateGoryIdSpecIdMap.get(shopBaseProductType.getType_category_id()); + String [] type_spec_ids=shopBaseProductType.getType_spec_ids().split(","); + for (String specId : type_spec_ids) { + specIdList.add(Integer.parseInt(specId)); + } + String typeSpecIds= specIdList.stream().map(String::valueOf).distinct().collect(Collectors.joining(",")); + shopBaseProductType.setType_spec_ids(typeSpecIds); + }else { + specIdList=cateGoryIdSpecIdMap.get(shopBaseProductType.getType_category_id()); + if(null!=specIdList){ + String typeSpecIds= specIdList.stream().map(String::valueOf).distinct().collect(Collectors.joining(",")); + shopBaseProductType.setType_spec_ids(typeSpecIds); + } + } + } + if(!shopBaseProductTypes.isEmpty()){//更新规格类型 + shopBaseProductTypeService.updateBatchById(shopBaseProductTypes,shopBaseProductTypes.size()); + } + //4、新增shopItem + List addShopProductItemList=new ArrayList<>(); + List updateShopProductItemList=new ArrayList<>(); + Map existProductItemMap= getExistShopItemList(shopProductItems,storeId); + shopProductItems.forEach(item->{ + item.setProduct_id(specsProductIdMap.get(item.getItem_barcode())); + StringBuilder pIspecItemIds= new StringBuilder(); + String[] pIspecItemNames=item.getItem_name().split(","); + List> spectItems=new ArrayList<>(); + List> specJson=new ArrayList<>(); + for (String pIspecItemName : pIspecItemNames) { + ShopProductSpecItem specItem = specItemMap.get(pIspecItemName); + Integer specItemId=specItem.getSpec_item_id(); + if (StringUtils.isNotEmpty(pIspecItemIds)) { + pIspecItemIds.append(",").append(specItemId); + } else { + pIspecItemIds.append(specItemId); + } + Map specItemJson=new HashMap<>(); + specItemJson.put("name",specItem.getSpec_item_name());//规格列表 + specItemJson.put("id",specItem.getSpec_item_id()); + spectItems.add(specItemJson); + + Map specJsonMap=new HashMap<>(); + specJsonMap.put("name",specItem.getSpecName());//规格名称 + specJsonMap.put("id",specItem.getSpec_id()); + specJson.add(specJsonMap); + } + item.setSpec_item_ids(pIspecItemIds.toString()); + String item_spec= ShopJsonUtils.generateJsonWithOrgJsonItemSpec(spectItems,specJson); + // String productInfoSpec= ShopJsonUtils.generateJsonWithOrgJsonProducSpec(spectItems,specJson); + item.setItem_spec(item_spec); + item.setItem_title(""); + item.setItem_freetime(0); + item.setCategory_id(specCateGoryMap.get(item.getItem_barcode())); + if(existProductItemMap.containsKey(item.getItem_name())){ + item.setItem_id(existProductItemMap.get(item.getItem_barcode())); + updateShopProductItemList.add(item); + }else { + addShopProductItemList.add(item); + } + }); + if(!addShopProductItemList.isEmpty()){ + List generatedIds = shopNumberSeqService.batchCreateNextNo("item_id", addShopProductItemList.size()); + for (int i = 0; i < generatedIds.size(); i++) { + addShopProductItemList.get(i).setItem_id(generatedIds.get(i)); + addShopProductItemList.get(i).setItem_src_id(String.valueOf(generatedIds.get(i))); + } + shopProductItemService.saveBatch(addShopProductItemList,addShopProductItemList.size()); + } + if(!updateShopProductItemList.isEmpty()){ + shopProductItemService.updateBatchById(updateShopProductItemList,updateShopProductItemList.size()); + } + List addShopProductItemSeqList=new ArrayList<>(); + addShopProductItemList.forEach(item->{ + ShopProductItemSeq shopProductItemSeq=new ShopProductItemSeq(); + shopProductItemSeq.setItem_id(item.getItem_id()); + shopProductItemSeq.setProduct_id(item.getProduct_id()); + String item_spec=item.getItem_spec(); + Long productId=item.getProduct_id(); + cn.hutool.json.JSONArray array_item_spec = cn.hutool.json.JSONUtil.parseArray(item_spec); + List spec_item_ids=new ArrayList<>(); + for (Object josn_item_spec : array_item_spec) { + cn.hutool.json.JSONObject itemJson = (cn.hutool.json.JSONObject) ((cn.hutool.json.JSONObject) josn_item_spec).get("item"); + Integer id = Convert.toInt(itemJson.get("id")); + if (ObjectUtil.isNotNull(id)) { + spec_item_ids.add(id); + } + } + if (CollUtil.isNotEmpty(spec_item_ids)) { + Collections.sort(spec_item_ids); + } + String sku_uniqid = "default"; + if(CollUtil.isNotEmpty(spec_item_ids)){ + sku_uniqid = CollUtil.join(spec_item_ids, "-"); + } + String product_item_seq_val = String.format("%s:%s", productId, sku_uniqid); + String product_item_seq_id = SecureUtil.md5(product_item_seq_val); + shopProductItemSeq.setProduct_item_seq_id(product_item_seq_id); + shopProductItemSeq.setProduct_item_seq_val(product_item_seq_val); + addShopProductItemSeqList.add(shopProductItemSeq); + }); + if(!addShopProductItemSeqList.isEmpty()){ + shopProductItemSeqService.saveOrUpdateBatch(addShopProductItemSeqList,addShopProductItemSeqList.size()); + } + //更新shopProductInfo + Map> shopInfoItems=new HashMap<>(); + List updateShopProductInfoList=new ArrayList<>(); + for (ShopProductItem shopProductItem: addShopProductItemList) { + Long productId=shopProductItem.getProduct_id(); + List shopProductItemList=new ArrayList<>(); + if(shopInfoItems.containsKey(shopProductItem.getProduct_id())){ + shopProductItemList=shopInfoItems.get(shopProductItem.getProduct_id()); + } + shopProductItemList.add(shopProductItem); + shopInfoItems.put(productId,shopProductItemList); + } + Map itemQualityMap=new HashMap<>(); + shopInfoItems.forEach((productId,sItemList)->{ + ShopProductInfo spui=new ShopProductInfo(); + spui.setProduct_id(productId); + String item_spec=sItemList.get(0).getItem_spec(); + cn.hutool.json.JSONArray array_item_spec = cn.hutool.json.JSONUtil.parseArray(item_spec); + List spec_item_ids=new ArrayList<>(); + for (Object josn_item_spec : array_item_spec) { + Integer specId = Convert.toInt(((cn.hutool.json.JSONObject) josn_item_spec).get("id")); + spec_item_ids.add(specId); + } + String spec_ids=CollUtil.join(spec_item_ids,","); + spui.setSpec_ids(spec_ids); + + Map specItemJson=new HashMap<>(); + cn.hutool.json.JSONArray productUniqidjsonArray=new cn.hutool.json.JSONArray(); + AtomicReference itemQuantity= new AtomicReference<>(0); + sItemList.forEach(item->{ + String itemSpec=item.getItem_spec(); + cn.hutool.json.JSONArray arrayItemSpec = cn.hutool.json.JSONUtil.parseArray(itemSpec); + cn.hutool.json.JSONObject uniqidItemJson=new cn.hutool.json.JSONObject(); + String specItemIdSpecItemId=""; + for (Object josn_item_spec : arrayItemSpec) { + Integer specId = Convert.toInt(((cn.hutool.json.JSONObject) josn_item_spec).get("id")); + Integer specName = Convert.toInt(((cn.hutool.json.JSONObject) josn_item_spec).get("name")); + String specIdName=specId+"-"+specName; + cn.hutool.json.JSONObject itemJson = (cn.hutool.json.JSONObject) ((cn.hutool.json.JSONObject) josn_item_spec).get("item"); + cn.hutool.json.JSONArray itemsJsonArray=new cn.hutool.json.JSONArray(); + if(specItemJson.containsKey(specIdName)){ + itemsJsonArray=specItemJson.get(specIdName); + } + itemsJsonArray.add(itemJson); + specItemJson.put(specIdName,itemsJsonArray); + if(StringUtils.isNotEmpty(specItemIdSpecItemId)){ + specItemIdSpecItemId=specItemIdSpecItemId+"-"+itemJson.getStr("id"); + }else { + specItemIdSpecItemId=itemJson.getStr("id"); + } + } + uniqidItemJson.set(specItemIdSpecItemId,new Object[]{item.getItem_id(),item.getItem_quantity(),"","1001"}); + productUniqidjsonArray.add(uniqidItemJson); + itemQuantity.updateAndGet(v -> v + item.getItem_quantity()); + }); + itemQualityMap.put(productId,itemQuantity.get()); + cn.hutool.json.JSONArray jsonArraySpecItem=new cn.hutool.json.JSONArray(); + specItemJson.forEach((key,jsonArray)->{ + cn.hutool.json.JSONObject itemJson = new cn.hutool.json.JSONObject(); + itemJson.set("item",jsonArray); + String[] specIdName=key.split("-"); + String specId=specIdName[0]; + String specName=specIdName[1]; + itemJson.set("name",specName); + itemJson.set("spec_format","text"); + itemJson.set("id",specId); + jsonArraySpecItem.add(itemJson); + }); + String product_spec= JSONUtil.toJsonStr(jsonArraySpecItem); + String product_uniqid=JSONUtil.toJsonStr(productUniqidjsonArray); + spui.setProduct_spec(product_spec); + spui.setProduct_uniqid(product_uniqid); + updateShopProductInfoList.add(spui); + }); + if(!updateShopProductInfoList.isEmpty()){ + shopProductInfoService.updateBatchById(updateShopProductInfoList,updateShopProductInfoList.size()); + } + //5、更新shopindex数量 + List shopProductIndexList=new ArrayList<>(); + itemQualityMap.forEach((productId,stock)->{ + ShopProductIndex shopProductIndex=new ShopProductIndex(); + shopProductIndex.setProduct_id(productId); + shopProductIndex.setProduct_quantity(stock); + shopProductIndexList.add(shopProductIndex); + }); + if(!shopProductIndexList.isEmpty()){ + shopProductIndexService.updateBatchById(shopProductIndexList,shopProductIndexList.size()); + } + + //最后保存 + if(!addShopBaseProductSpecList.isEmpty()){//规格大类 + shopBaseProductSpecService.saveBatch(addShopBaseProductSpecList,addShopBaseProductSpecList.size()); + } + if(!updateShopBaseProductSpecList.isEmpty()){//规格大类 + shopBaseProductSpecService.updateBatchById(updateShopBaseProductSpecList,updateShopBaseProductSpecList.size()); + } + if(!addSpecItemList.isEmpty()){//规格值 + shopProductSpecItemService.saveBatch(addSpecItemList,addSpecItemList.size()); + } + if(!updateSpecItemList.isEmpty()){//规格值 + shopProductSpecItemService.updateBatchById(updateSpecItemList,updateSpecItemList.size()); + } + //shopitem 最后删除没有规格的商品 + QueryWrapper queryWrapper=new QueryWrapper<>(); + queryWrapper.select("item_id"); + for (ShopProductIndex shopProductIndex:shopProductIndexList) { + queryWrapper.or(q->q.eq("store_id",storeId) + .eq("product_id",shopProductIndex.getProduct_id()) + .and(wrapper->wrapper.eq("item_name","") + .or() + .isNull("item_name"))); + } + List queryShopProductItemList=shopProductItemService.list(queryWrapper); + if(!queryShopProductItemList.isEmpty()){ + List ItemIds= queryShopProductItemList.stream().map(ShopProductItem::getItem_id).collect(Collectors.toList()); + QueryWrapper shopProductItemSeqQueryWrapper=new QueryWrapper<>(); + shopProductItemSeqQueryWrapper.select("product_item_seq_id"); + shopProductItemSeqQueryWrapper.eq("item_id",ItemIds); + List rmSeq= shopProductItemSeqService.list(shopProductItemSeqQueryWrapper); + List rmSeqIds=rmSeq.stream().map(ShopProductItemSeq::getProduct_item_seq_id).collect(Collectors.toList()); + shopProductItemSeqService.removeBatchByIds(rmSeqIds); + shopProductItemService.removeBatchByIds(ItemIds,ItemIds.size()); + } + }catch (Exception e){ + status.setRollbackOnly(); + log.error("批量操作异常", e); + return Pair.of(false, "批量操作异常: " + e.getMessage()); + } + return "success"; + }); + } + + + + /** + * 查询分类规格 + * @param shopBaseProductSpecs + * @param storeId + * @return + */ + private Map getExistShopBaseSpec(List shopBaseProductSpecs,Integer storeId){ + Map result=new HashMap<>(); + QueryWrapper wrapper = new QueryWrapper<>(); + for (ShopBaseProductSpec shopBaseProductSpec : shopBaseProductSpecs) { + wrapper.or(q->q.eq("spec_name",shopBaseProductSpec.getSpec_name()).eq("store_id",storeId)); + } + List shopBaseProductSpecList=shopBaseProductSpecService.list(wrapper); + for (ShopBaseProductSpec shopBaseProductSpec : shopBaseProductSpecList) { + result.put(shopBaseProductSpec.getSpec_name(),shopBaseProductSpec.getSpec_id()); + } + return result; + } + + /** + * 查询分类规格 + * @param shopProductSpecItems + * @param storeId + * @return + */ + private Map getExistShopSpecItem(List shopProductSpecItems,Integer storeId){ + Map result=new HashMap<>(); + QueryWrapper wrapper = new QueryWrapper<>(); + for (ShopProductSpecItem shopProductSpecItem : shopProductSpecItems) { + wrapper.or(q->q.eq("spec_item_name",shopProductSpecItem.getSpec_item_name()).eq("store_id",storeId)); + } + List shopProductSpecItemList=shopProductSpecItemService.list(wrapper); + for (ShopProductSpecItem shopProductSpecItem : shopProductSpecItemList) { + result.put(shopProductSpecItem.getSpec_item_name(),shopProductSpecItem.getSpec_item_id()); + } + return result; + } + + /** + * 查找分类的类型 + * @param syncStoreSpecsList + * @param storeId + * @return + */ + private List getShopBaseProductTypeList(List syncStoreSpecsList,Integer storeId){ + QueryWrapper wrapper = new QueryWrapper<>(); + for (SyncStoreSpecs syncStoreSpecs : syncStoreSpecsList) { + wrapper.or(q->q.eq("store_id",storeId).eq("type_category_id",syncStoreSpecs.getCategoryId())); + } + return shopBaseProductTypeService.list(wrapper); + } + + /** + * 查找分类的类型 + * @param syncStoreSpecsList + * @param storeId + * @return + */ + private Map getExistShopItemList(List syncStoreSpecsList,Integer storeId){ + QueryWrapper wrapper = new QueryWrapper<>(); + for (ShopProductItem shopProductItem : syncStoreSpecsList) { + wrapper.or(q->q.eq("store_id",storeId) + .eq("item_barcode",shopProductItem.getItem_barcode()) + .eq("item_name",shopProductItem.getItem_name())); + } + List shopProductItemList=shopProductItemService.list(wrapper); + Map result=new HashMap<>(); + for (ShopProductItem shopProductItem : shopProductItemList) { + result.put(shopProductItem.getItem_name(),shopProductItem.getItem_id()); + } + return result; + } + + public static void main(String[] args) { + List spec_item_ids= Arrays.asList("12","13","14","15","16","17","18","19","20"); + System.out.println(CollUtil.join(spec_item_ids, "-")); + } +} diff --git a/mall-shop/src/main/resources/mapper/sync/SyncStoreSpecsMapper.xml b/mall-shop/src/main/resources/mapper/sync/SyncStoreSpecsMapper.xml new file mode 100644 index 00000000..5c011c77 --- /dev/null +++ b/mall-shop/src/main/resources/mapper/sync/SyncStoreSpecsMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/sql/shop/dev/20250819_ddl.sql b/sql/shop/dev/20250819_ddl.sql new file mode 100644 index 00000000..f1f9417d --- /dev/null +++ b/sql/shop/dev/20250819_ddl.sql @@ -0,0 +1,13 @@ +CREATE TABLE `sycn_store_specs` ( + `sycn_specs_id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '多规格编号', + `json_specs` json DEFAULT NULL COMMENT '多规格json数据,如[{"spec": [{"name": "颜色","value": "红色"},{"name": "尺寸","value": "10"}],"priceAndstock": {"price": 100,"stock": 10}},{"spec": [{ "name": "颜色","value": "黑色"},{"name": "尺寸","value": "30"}], +"priceAndstock": {"price": 300,"stock": 30}}]', + `product_number` varchar(50) DEFAULT '' COMMENT '条形码', + `store_id` int unsigned NOT NULL DEFAULT '0' COMMENT '店铺编号', + `is_deal` varchar(1) NOT NULL DEFAULT '0' COMMENT '是否处理完成,1:是,0:否', + `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '新建时间', + `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`sycn_specs_id`) USING BTREE, + KEY `index_store_id` (`store_id`) USING BTREE, + KEY `index_is_deal` (`is_deal`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='多规格数据表'; \ No newline at end of file