导入多规格匹配功能新增,数据清醒

This commit is contained in:
liyj 2025-08-22 18:37:44 +08:00
parent 23a804ff9f
commit 585a57764f
22 changed files with 1347 additions and 48 deletions

View File

@ -72,4 +72,7 @@ public class ShopProductSpecItem implements Serializable {
@TableField(exist=false)
private boolean isUpdate;
@ApiModelProperty(value = "规格名称")
@TableField(exist=false)
private String specName;
}

View File

@ -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;
}

View File

@ -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<String,Pattern> PROTECT_PATTERNS = new HashMap<String,Pattern>(){{
put(PROTECT_UNIT,UNIT_REGEX);
@ -65,6 +66,10 @@ public class JiebaUtils {
put("dot","\\.");
}};
public static final Set<String> 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<originalINdex2){
// 使用中文保护标记包裹原始值
String protectedText = PROTECT_START + original+ PROTECT_END;
matcher.appendReplacement(sb, Matcher.quoteReplacement(protectedText));
matcher.appendTail(sb);
}else {
String protectedText = PROTECT_START +original2+original + PROTECT_END;
matcher2.appendReplacement(sb, Matcher.quoteReplacement(protectedText));
matcher2.appendTail(sb);
}
}
}else if(special||unit){
Matcher matcher = UNIT_CHN_REGEX.matcher(text);
if(matcher.find()) {
String original = matcher.group();
@ -139,7 +160,8 @@ public class JiebaUtils {
matcher.appendReplacement(sb, Matcher.quoteReplacement(protectedText));
matcher.appendTail(sb);
}
} else {
}else {
stop:
for (Map.Entry<String, Pattern> 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<String> 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<String> 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<String> words = JiebaUtils.extractKeywords(text);
System.out.println(words);
// List<String> words = JiebaUtils.extractKeywords(text);
//System.out.println(words);
Map<String,String> shopMap= JiebaUtils.getShopDetails(ProductTitleUtil.cleanTitle2(text));
System.out.println(shopMap);

View File

@ -53,14 +53,17 @@ public class ProductTitleUtil {
Arrays.asList("华为", "苹果", "小米", "三星", "美的", "格力", "耐克", "阿迪达斯", "海尔","雀巢","伊利","蒙牛","达能","乐事","多力多滋","三只松鼠","良品铺子","可口可乐",
"农夫山泉","元气森林","红牛","雅诗兰黛","欧莱雅","玉兰油","科颜氏","宝洁","汰渍","帮宝适","联合利华","Unilever","含多芬","清扬","大窑","谢村桥牌阡糯","谢村桥牌阡",
"六个核桃","大豫竹","优乐多","安慕希","纳爱斯","舒客","宜轩", "蓝月亮","海尔","美的","松下","戴森","耐克","安踏","李宁","特仑苏","纯甄","安井","三全","哇哈哈",
"龙江家园","达利园","春光","妙芙","南星","利嘉旺","卡得福","泓一","爱乡亲","思念","得力","中雪","江南点心局","德庄","六指鼠",
"依水塬","乌苏啤酒","阿尔卑斯", "瑞旗","振雷","中狗","宝视达","冷酸灵","骆驼","NIKE","PAMU","康师傅","信智利","双兔","安足莱","新博美","新博","创利")
"龙江家园","达利园","春光","妙芙","南星","利嘉旺","卡得福","泓一","爱乡亲","思念","得力","中雪","江南点心局","德庄","六指鼠","娃哈哈","开古","不二家","湘亮牌",
"依水塬","乌苏啤酒","阿尔卑斯", "瑞旗","振雷","中狗","宝视达","冷酸灵","骆驼","NIKE","PAMU","康师傅","信智利","双兔","安足莱","新博美","湘亮牌","忆江南","张骞牌",
"新博","创利","哈奇利","好口福","银鹭","开古","百事可乐","邛池","天旭牌","泗泉山","银狼","象芽王","拜将坛","健民","湘亮牌","周大黑","盛华牌","盛华","碗碗香","龙凤王",
"陕南健源","百州红","八度名苑","旭美","今麦郞","勇闯天涯","青岛","一起赢","元気森林","云南红茶","盈亮","树堂","乡糯香","汉中魏","金源","疆丝麦耘","四海油","亿家康",
"利民")
));
/**
* 品类词库初始化后不可变
*/
public static final Set<String> CATEGORY_LIBRARY = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList("手机", "电脑", "空调", "冰箱", "运动鞋", "T恤", "洗发水", "洗衣液","猪脚")
Arrays.asList("手机", "电脑", "空调", "冰箱", "运动鞋", "T恤", "洗发水", "洗衣液","猪脚","花生牛奶")
));
static {
@ -88,7 +91,8 @@ public class ProductTitleUtil {
//特殊商品
public static final Set<String> SPECIAL_NAME = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList("水饺", "面条", "包子","卷纸","卫生纸","紫菜汤","猪肉包","叉烧包","香菇青菜包","面包","黑米")
Arrays.asList("水饺", "面条", "包子","卷纸","卫生纸","紫菜汤","猪肉包","叉烧包","香菇青菜包","面包","黑米","棒棒糖","梳子","谢村花雕","古秦洋精","通裕梨花",
"台式烤香肠")
));
static {
@ -104,8 +108,9 @@ public class ProductTitleUtil {
Arrays.asList("","","","","","")
));
public static final Set<String> UNITS = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList("g","","kg","千克","ml","毫升","l","","cm","厘米","mm","毫米","","","","")
Arrays.asList("g","","kg","千克","ml","毫升","l","","cm","厘米","mm","毫米","","","","","L")
));
private ProductTitleUtil() {
}

View File

@ -33,4 +33,106 @@ PAMU 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
鸡蛋面片 500 n
疆丝麦耘 500 n
亿家康 500 n
汉中香米 500 n
菜籽油 500 n

View File

@ -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) // 自动确定模糊度

View File

@ -33,4 +33,8 @@ public interface ShopNumberSeqService extends IBaseService<ShopNumberSeq> {
List<Integer> getBatchUserAccountBaseId(int batchSize);
void clearKeyStoreAccountBaseId();
List<Integer> getBatchSpecId(int batchSize);
void clearKeyStoreSepcId();
}

View File

@ -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<ShopNumberSeqMappe
@Autowired
private ShopProductSpecItemService shopProductSpecItemService;
@Autowired
private ShopBaseProductSpecService shopBaseProductSpecService;
@Autowired
private AccountService accountService;
@ -208,7 +213,9 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
redisService.del(RedisKey.STOREDATACCOUNTBASEID);
}
public void clearKeyStoreSepcId(){
redisService.del(RedisKey.STOREDATASPECID);
}
/**
* 清除缓存专门给并发使用,防止redis的缓存没有加载
@ -244,6 +251,33 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
edit(seq);
}
/**
* 存的是最大值取的是范围如存最大值1批量是2则取范围1+11+2,如果没有则从1开始算即取范围[0+1,0+2]
* @param batchSize
* @return
*/
@Override
public synchronized List<Integer> 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<ShopBaseProductSpec> 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+11+2,如果没有则从1开始算即取范围[0+1,0+2]
* @param batchSize
@ -272,6 +306,7 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
}
/**
* 存的是最大值取的是范围如存最大值1批量是2则取范围1+11+2,如果没有则从1开始算即取范围[0+1,0+2]
* @param batchSize

View File

@ -13,6 +13,7 @@ import com.suisung.mall.common.modules.page.ShopPageApp;
import com.suisung.mall.common.modules.store.ShopStoreInfo;
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.service.CloundService;
import com.suisung.mall.shop.base.service.AccountBaseConfigService;
@ -71,7 +72,7 @@ public class ShopPageAppController extends BaseControllerImpl {
@RequestParam(name = "pageSize", defaultValue = "100") Integer pageSize) {
Map<String, Object> 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._("用户信息异常!"));
}

View File

@ -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._("用户信息异常!"));
}

View File

@ -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("品牌描述")

View File

@ -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;
}

View File

@ -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<SpecItem> spec;
private PriceStockItem priceAndstock;
public ProductSpecInfo() {
this.spec = new ArrayList<>();
}
// Getters and Setters
public List<SpecItem> getSpec() { return spec; }
public void setSpec(List<SpecItem> spec) { this.spec = spec; }
public PriceStockItem getPriceAndstock() { return priceAndstock; }
public void setPriceAndstock(PriceStockItem priceAndstock) {
this.priceAndstock = priceAndstock;
}
}
// 商品规格映射表商品ID -> JSON字符串
private Map<String, List<String>> specsMap;
private static Gson gson = new GsonBuilder().setPrettyPrinting().create();
public Map<String, List<String>> getSpecsMap() {
return specsMap;
}
public void setSpecsMap(Map<String, List<String>> specsMap) {
this.specsMap = specsMap;
}
/**
* 添加商品规格
*
* @param productId 商品ID
* @param specItems 规格项列表
* @param price 价格
* @param stock 库存
*/
public void addProductSpec(String productId, List<SpecItem> 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<String> getSepcMapValue(String productId) {
if (!specsMap.containsKey(productId)) {
return Collections.emptyList();
}
return specsMap.get(productId);
}
/**
* 获取商品所有规格信息
*
* @param productId 商品ID
* @return 商品规格信息列表
*/
public List<ProductSpecInfo> getProductSpecs(String productId) {
if (!specsMap.containsKey(productId)) {
return Collections.emptyList();
}
List<ProductSpecInfo> 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<SpecItem> parseSpecString(String specStr) {
List<SpecItem> 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;
}
}

View File

@ -20,4 +20,7 @@ public class RedisKey {
public static final String STOREDATACCOUNTBASEID="storedata:accountBaseId";
public static final String STOREDATASPECID="storedata:SpecId";
}

View File

@ -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;
@ -18,8 +23,6 @@ import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@Component
@ -32,6 +35,8 @@ public class ShopBatchSubmitListener extends AnalysisEventListener<SxGoosModelEx
private SyncThirdDataService syncThirdDataService;
private SyncStoreSpecsService syncStoreSpecsService;
// 线程池配置
private final ExecutorService executorService;
@ -51,9 +56,11 @@ public class ShopBatchSubmitListener extends AnalysisEventListener<SxGoosModelEx
@Getter
private Map<String,Integer> 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);
@ -62,6 +69,9 @@ public class ShopBatchSubmitListener extends AnalysisEventListener<SxGoosModelEx
this.success = new AtomicInteger();
this.fails = new AtomicInteger();
this.batchSize= new AtomicInteger();
this.productSpecManager=new ProductSpecManager();
Map<String, List<String>> specsMap=new HashMap<>();
productSpecManager.setSpecsMap(specsMap);
}
@Override
@ -74,6 +84,7 @@ public class ShopBatchSubmitListener extends AnalysisEventListener<SxGoosModelEx
submitBatch();
// 提交后清空缓存
cachedDataList.clear();
productSpecManager.getSpecsMap().clear();
}
}
}
@ -86,6 +97,7 @@ public class ShopBatchSubmitListener extends AnalysisEventListener<SxGoosModelEx
batchSize.incrementAndGet();
submitBatch();
cachedDataList.clear();
productSpecManager.getSpecsMap().clear();
}
}
// 等待所有任务完成
@ -105,6 +117,7 @@ public class ShopBatchSubmitListener extends AnalysisEventListener<SxGoosModelEx
private void submitBatch() {
// 复制当前批次数据避免异步修改
List<SxGoosModelExcel> batchCopy = new ArrayList<>(deduplicateById(cachedDataList));
Map<String,List<String>> sepcsMap = new HashMap<>(productSpecManager.getSpecsMap());
log.info("去重前:{};去重后:{}" , cachedDataList.size(), batchCopy.size());
final int index = batchSize.get();
futures.add(executorService.submit(()->{
@ -118,6 +131,7 @@ public class ShopBatchSubmitListener extends AnalysisEventListener<SxGoosModelEx
syncThirdDataService.baseSaveOrUpdateGoodsBatch(jsonArray,storeId,isNegativeAllowed,brandMaps);
log.info("已提交批次: {} 条", batchCopy.size());
success.getAndIncrement();
saveSnycStoreSpec(sepcsMap);
return "完成批次:"+index;
} catch (Exception e) {
if(i<2){
@ -155,10 +169,121 @@ public class ShopBatchSubmitListener extends AnalysisEventListener<SxGoosModelEx
}
sxGoosModelExcel.setProduct_number(sxGoosModelExcel.getProduct_barcode());
sxGoosModelExcel.setOriginal_price(sxGoosModelExcel.getRetail_price());
if(StringUtils.isNotEmpty(sxGoosModelExcel.getShop_specs())){
List<ProductSpecManager.SpecItem> 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<String,List<String>> listMap){
if(listMap==null){
return;
}
//List<SyncStoreSpecs> syncStoreSpecs=new ArrayList<>();
List<SyncStoreSpecs> addSyncStoreSpecs=new ArrayList<>();
List<SyncStoreSpecs> updateSyncStoreSpecs=new ArrayList<>();
Map<String,Long> 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<String,Long> getExistProductId(Map<String,List<String>> listMap){
QueryWrapper<SyncStoreSpecs> queryWrapper = new QueryWrapper<>();
listMap.forEach((k,v)->{
queryWrapper.or(q->q.eq("store_id",storeId).eq("product_number",k));
});
List<SyncStoreSpecs> syncStoreSpecs = syncStoreSpecsService.list(queryWrapper);
if(syncStoreSpecs==null){
return null;
}
Map<String,Long> 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<SxGoosModelExcel> sxGoosModelExcelList = new ArrayList<>();
sxGoosModelExcelList.add(sxGoosModelExcel);
sxGoosModelExcelList.add(sxGoosModelExcel1);
sxGoosModelExcelList.add(sxGoosModelExcel3);
ProductSpecManager productSpecManager=new ProductSpecManager();
Map<String, List<String>> specsMap=new HashMap<>();
productSpecManager.setSpecsMap(specsMap);
sxGoosModelExcelList=sxGoosModelExcelList.stream().peek(gm -> {
if(StringUtils.isNotEmpty(gm.getJsonSpecs())){
List<ProductSpecManager.SpecItem> 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()));
}
}

View File

@ -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<SyncStoreSpecs> {
List<SyncStoreSpecs> findSyncStoreSpecsList(SyncStoreSpecs syncStoreSpecs,@Param("limit") Integer limit,@Param("pageSize") Integer pageSize);
}

View File

@ -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<SyncStoreSpecs> {
void dealSyncStoreSpecs(Integer storeId);
}

View File

@ -130,7 +130,7 @@ public class ProductMappingServiceImpl extends BaseServiceImpl<ProductMappingMap
@Override
public void computeProductMapping(List<ShopProductBase> 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<ProductMappingMap
if(null!=shopProductSpecItem){
item_spec=shopProductSpecItem.getItem_spec();
productSpec=shopProductSpecItem.getProduct_spec();
item.setItem_name(shopProductSpecItem.getSpec_item_name());
if(isUpdate){
item.setItem_unit_price(shopProductSpecItem.getItemPrice());
}else {

View File

@ -17,10 +17,7 @@ import com.suisung.mall.shop.number.service.ShopNumberSeqService;
import com.suisung.mall.shop.sync.excleHandle.TemplateStyleHandler;
import com.suisung.mall.shop.sync.exelModel.*;
import com.suisung.mall.shop.sync.listen.ShopBatchSubmitListener;
import com.suisung.mall.shop.sync.service.ProductMappingService;
import com.suisung.mall.shop.sync.service.ShopSyncImportService;
import com.suisung.mall.shop.sync.service.StoreDbConfigService;
import com.suisung.mall.shop.sync.service.SyncThirdDataService;
import com.suisung.mall.shop.sync.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@ -58,6 +55,8 @@ public class ShopSyncImportServiceImpl implements ShopSyncImportService {
private ShopNumberSeqService shopNumberSeqService;
@Autowired
private ShopBaseProductCategoryService shopBaseProductCategoryService;
@Autowired
private SyncStoreSpecsService syncStoreSpecsService;
private final int limitCnt = 100;
@ -224,11 +223,12 @@ public class ShopSyncImportServiceImpl implements ShopSyncImportService {
storeDbConfigQueryWrapper.eq("store_id", storeId);
StoreDbConfig storeDbConfig = storeDbConfigService.getOne(storeDbConfigQueryWrapper);
String isNegativeAllowed = storeDbConfig.getIsNegativeAllowed();
ShopBatchSubmitListener shopBatchSubmitListener=new ShopBatchSubmitListener(syncThirdDataService);
ShopBatchSubmitListener shopBatchSubmitListener=new ShopBatchSubmitListener(syncThirdDataService,syncStoreSpecsService);
shopBatchSubmitListener.setStoreId(storeId);
shopBatchSubmitListener.setBrandMaps(brandMaps);
shopBatchSubmitListener.setIsNegativeAllowed(isNegativeAllowed);
EasyExcel.read(filePath,SxGoosModelExcel.class,shopBatchSubmitListener).sheet().doRead();
syncStoreSpecsService.dealSyncStoreSpecs(Integer.valueOf(storeId));
productMappingService.syncAllProductMapping(Integer.valueOf(storeId), DicEnum.YESORNO_0.getCode());
syncThirdDataService.syncShopImages(Integer.valueOf(storeId));
cleanCache(storeId);//清理redis数据

View File

@ -0,0 +1,593 @@
package com.suisung.mall.shop.sync.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.suisung.mall.common.enums.DicEnum;
import com.suisung.mall.common.modules.base.ShopBaseProductSpec;
import com.suisung.mall.common.modules.base.ShopBaseProductType;
import com.suisung.mall.common.modules.product.*;
import com.suisung.mall.common.modules.sync.SyncStoreSpecs;
import com.suisung.mall.common.utils.StringUtils;
import com.suisung.mall.shop.base.service.ShopBaseProductSpecService;
import com.suisung.mall.shop.base.service.ShopBaseProductTypeService;
import com.suisung.mall.shop.number.service.ShopNumberSeqService;
import com.suisung.mall.shop.product.service.*;
import com.suisung.mall.shop.sixun.utils.CommonUtil;
import com.suisung.mall.shop.sync.Utils.ShopJsonUtils;
import com.suisung.mall.shop.sync.keymanage.ProductSpecManager;
import com.suisung.mall.shop.sync.mapper.SyncStoreSpecsMapper;
import com.suisung.mall.shop.sync.service.SyncStoreSpecsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@Service
@lombok.extern.slf4j.Slf4j
@Transactional
public class SyncStoreSpecsServiceImpl extends ServiceImpl<SyncStoreSpecsMapper, SyncStoreSpecs> 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<Future<?>> futures = new ArrayList<>();
QueryWrapper<SyncStoreSpecs> 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<SyncStoreSpecs> 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<SyncStoreSpecs> 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<String,List<String>> specMap=new HashMap<>();//存储规格类型
Map<String,List<String>> specValueMap=new HashMap<>();//存储规格的值
Map<String,Integer> specCateGoryMap=new HashMap<>();//存储分类id
Map<String,Long> specsProductIdMap=new HashMap<>();//存储product_id
//Map<String,Long> existProductItemMap=new HashMap<>();//存储item_id
Gson gson=new Gson();
//规格定义
List<ShopBaseProductSpec> shopBaseProductSpecList=new ArrayList<>();
//规格值定义
//解析json
for (SyncStoreSpecs syncStoreSpecs : syncStoreSpecsList) {
List<String> 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<ShopProductItem> shopProductItems=new ArrayList<>();
specMap.forEach((k,v)->{
List<ProductSpecManager.ProductSpecInfo> specInfos= productSpecManager.getProductSpecs(k);
for (ProductSpecManager.ProductSpecInfo specInfo : specInfos) {
List<ProductSpecManager.SpecItem> 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<String> 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<ShopBaseProductSpec> addShopBaseProductSpecList=new ArrayList<>();
List<ShopBaseProductSpec> updateShopBaseProductSpecList=new ArrayList<>();
List<ShopBaseProductSpec> shopBaseProductSpecs=shopBaseProductSpecList.stream()
.filter(com.suisung.mall.common.utils.CommonUtil.distinctByKey(ShopBaseProductSpec::getSpec_name))
.collect(Collectors.toList());
Map<String,Integer> existsMap= getExistShopBaseSpec(shopBaseProductSpecs,storeId);
Map<String,Integer> specCateGoryIdMap=new HashMap<>();
Map<Integer,List<Integer>> 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<Integer> specIds= shopNumberSeqService.getBatchSpecId(addShopBaseProductSpecList.size());//计算specId
for (int i=0;i<specIds.size();i++){
addShopBaseProductSpecList.get(i).setSpec_id(specIds.get(i));
List<Integer> 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<ShopProductSpecItem> 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<String,ShopProductSpecItem> specItemMap=new HashMap<>();//存储specItemValue对应的ShopProductSpecItem
List<ShopProductSpecItem> specItemList=specItems.stream()
.filter(com.suisung.mall.common.utils.CommonUtil.distinctByKey(ShopProductSpecItem::getSpec_item_name))
.collect(Collectors.toList());//去重
List<ShopProductSpecItem> addSpecItemList=new ArrayList<>();
List<ShopProductSpecItem> updateSpecItemList=new ArrayList<>();
Map<String,Integer> 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<Integer> specItemIds= shopNumberSeqService.getBatchSpecItemId(addSpecItemList.size());//计算specId
for (int i=0;i<specItemIds.size();i++){
addSpecItemList.get(i).setSpec_item_id(specItemIds.get(i));
specItemMap.put(addSpecItemList.get(i).getSpec_item_name(),addSpecItemList.get(i));
}
// shopProductSpecItemService.saveBatch(addSpecItemList,addSpecItemList.size());
}
// if(!updateSpecItemList.isEmpty()){
// shopProductSpecItemService.updateBatchById(updateSpecItemList,updateSpecItemList.size());
// }
//3新增规格类型
List<ShopBaseProductType> shopBaseProductTypes= getShopBaseProductTypeList(syncStoreSpecsList,storeId);
for(ShopBaseProductType shopBaseProductType:shopBaseProductTypes){
List<Integer> 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<ShopProductItem> addShopProductItemList=new ArrayList<>();
List<ShopProductItem> updateShopProductItemList=new ArrayList<>();
Map<String,Long> 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<Map<String, Object>> spectItems=new ArrayList<>();
List<Map<String, Object>> 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<String,Object> specItemJson=new HashMap<>();
specItemJson.put("name",specItem.getSpec_item_name());//规格列表
specItemJson.put("id",specItem.getSpec_item_id());
spectItems.add(specItemJson);
Map<String,Object> 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<Long> 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<ShopProductItemSeq> 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<Integer> 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<Long, List<ShopProductItem>> shopInfoItems=new HashMap<>();
List<ShopProductInfo> updateShopProductInfoList=new ArrayList<>();
for (ShopProductItem shopProductItem: addShopProductItemList) {
Long productId=shopProductItem.getProduct_id();
List<ShopProductItem> shopProductItemList=new ArrayList<>();
if(shopInfoItems.containsKey(shopProductItem.getProduct_id())){
shopProductItemList=shopInfoItems.get(shopProductItem.getProduct_id());
}
shopProductItemList.add(shopProductItem);
shopInfoItems.put(productId,shopProductItemList);
}
Map<Long,Integer> 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<Integer> 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<String,cn.hutool.json.JSONArray> specItemJson=new HashMap<>();
cn.hutool.json.JSONArray productUniqidjsonArray=new cn.hutool.json.JSONArray();
AtomicReference<Integer> 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<ShopProductIndex> 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<ShopProductItem> 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<ShopProductItem> queryShopProductItemList=shopProductItemService.list(queryWrapper);
if(!queryShopProductItemList.isEmpty()){
List<Long> ItemIds= queryShopProductItemList.stream().map(ShopProductItem::getItem_id).collect(Collectors.toList());
QueryWrapper<ShopProductItemSeq> shopProductItemSeqQueryWrapper=new QueryWrapper<>();
shopProductItemSeqQueryWrapper.select("product_item_seq_id");
shopProductItemSeqQueryWrapper.eq("item_id",ItemIds);
List<ShopProductItemSeq> rmSeq= shopProductItemSeqService.list(shopProductItemSeqQueryWrapper);
List<String> 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<String,Integer> getExistShopBaseSpec(List<ShopBaseProductSpec> shopBaseProductSpecs,Integer storeId){
Map<String,Integer> result=new HashMap<>();
QueryWrapper<ShopBaseProductSpec> wrapper = new QueryWrapper<>();
for (ShopBaseProductSpec shopBaseProductSpec : shopBaseProductSpecs) {
wrapper.or(q->q.eq("spec_name",shopBaseProductSpec.getSpec_name()).eq("store_id",storeId));
}
List<ShopBaseProductSpec> 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<String,Integer> getExistShopSpecItem(List<ShopProductSpecItem> shopProductSpecItems,Integer storeId){
Map<String,Integer> result=new HashMap<>();
QueryWrapper<ShopProductSpecItem> wrapper = new QueryWrapper<>();
for (ShopProductSpecItem shopProductSpecItem : shopProductSpecItems) {
wrapper.or(q->q.eq("spec_item_name",shopProductSpecItem.getSpec_item_name()).eq("store_id",storeId));
}
List<ShopProductSpecItem> 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<ShopBaseProductType> getShopBaseProductTypeList(List<SyncStoreSpecs> syncStoreSpecsList,Integer storeId){
QueryWrapper<ShopBaseProductType> 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<String,Long> getExistShopItemList(List<ShopProductItem> syncStoreSpecsList,Integer storeId){
QueryWrapper<ShopProductItem> 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<ShopProductItem> shopProductItemList=shopProductItemService.list(wrapper);
Map<String,Long> result=new HashMap<>();
for (ShopProductItem shopProductItem : shopProductItemList) {
result.put(shopProductItem.getItem_name(),shopProductItem.getItem_id());
}
return result;
}
public static void main(String[] args) {
List<String> spec_item_ids= Arrays.asList("12","13","14","15","16","17","18","19","20");
System.out.println(CollUtil.join(spec_item_ids, "-"));
}
}

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.suisung.mall.shop.sync.mapper.SyncStoreSpecsMapper">
<resultMap id="syncStoreSpecsMap" type="com.suisung.mall.common.modules.sync.SyncStoreSpecs">
<result property="sycnSpecsId" column="sycn_specs_id"/>
<result property="productNumber" column="product_number"/>
<result property="jsonSpecs" column="json_specs"/>
<result property="storeId" column="store_id"/>
<result property="isDeal" column="is_deal"/>
<result property="productId" column="product_id"/>
<result property="categoryId" column="category_id"/>
<result property="itemId" column="item_id"/>
</resultMap>
<select id="findSyncStoreSpecsList" resultMap="syncStoreSpecsMap">
SELECT s.sycn_specs_id ,
s.product_number,
s.json_specs ,
s.store_id,
s.is_deal,
spi.product_id,
spi.category_id,
spi.item_id
from sycn_store_specs s
left join shop_product_item spi
on s.product_number =spi.item_barcode
<where>
s.store_id=#{syncStoreSpecs.storeId}
and
s.is_deal=#{syncStoreSpecs.isDeal}
</where>
limit #{limit},#{pageSize}
</select>
</mapper>

View File

@ -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='多规格数据表';