1
This commit is contained in:
@@ -163,53 +163,108 @@ public class BatchPublishServiceImpl implements IBatchPublishService
|
||||
log.info("提取的URLs: {}", urls);
|
||||
log.info("提取的SKUIDs: {}", skuids);
|
||||
|
||||
// 查询商品详情
|
||||
// 查询商品详情(保证与输入URL数量一致且顺序一致;失败使用占位)
|
||||
List<Map<String, Object>> products = new ArrayList<>();
|
||||
Set<String> processedSkuids = new HashSet<>();
|
||||
|
||||
// 优先处理URL(更准确)
|
||||
for (int i = 0; i < urls.size(); i++) {
|
||||
String url = urls.get(i);
|
||||
try {
|
||||
log.info("正在处理第 {}/{} 个URL: {}", i + 1, urls.size(), url);
|
||||
Map<String, Object> productInfo = queryProductInfo(url);
|
||||
|
||||
Map<String, Object> productInfo = queryProductInfoWithRetry(url, 2, 200);
|
||||
|
||||
if (productInfo == null) {
|
||||
log.warn("URL解析返回空结果: {}", url);
|
||||
log.warn("URL解析失败,使用占位返回: {}", url);
|
||||
Map<String, Object> placeholder = new HashMap<>();
|
||||
placeholder.put("skuid", null);
|
||||
placeholder.put("productName", "未解析:" + url);
|
||||
placeholder.put("price", null);
|
||||
placeholder.put("productImage", null);
|
||||
placeholder.put("shopName", null);
|
||||
placeholder.put("shopId", null);
|
||||
placeholder.put("commission", null);
|
||||
placeholder.put("commissionShare", null);
|
||||
placeholder.put("commissionInfo", null);
|
||||
placeholder.put("materialUrl", url);
|
||||
placeholder.put("images", Collections.emptyList());
|
||||
placeholder.put("wenan", Collections.emptyList());
|
||||
placeholder.put("_raw", Collections.singletonMap("sourceUrl", url));
|
||||
placeholder.put("parseFailed", true);
|
||||
products.add(placeholder);
|
||||
continue;
|
||||
}
|
||||
|
||||
String skuid = (String) productInfo.get("skuid");
|
||||
if (skuid == null || skuid.trim().isEmpty()) {
|
||||
log.warn("商品SKUID为空,URL: {}, 返回数据: {}", url, productInfo);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (processedSkuids.contains(skuid)) {
|
||||
log.info("SKUID已存在,跳过: {}", skuid);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// 不对URL阶段做去重,保持与输入一致(允许同款多条)
|
||||
products.add(productInfo);
|
||||
processedSkuids.add(skuid);
|
||||
log.info("成功添加商品: SKUID={}, 名称={}", skuid, productInfo.get("productName"));
|
||||
|
||||
Object skuObj = productInfo.get("skuid");
|
||||
if (skuObj != null) {
|
||||
processedSkuids.add(String.valueOf(skuObj));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("查询商品信息失败,URL: {}", url, e);
|
||||
log.error("查询商品信息异常(占位返回),URL: {}", url, e);
|
||||
Map<String, Object> placeholder = new HashMap<>();
|
||||
placeholder.put("skuid", null);
|
||||
placeholder.put("productName", "未解析:" + url);
|
||||
placeholder.put("price", null);
|
||||
placeholder.put("productImage", null);
|
||||
placeholder.put("shopName", null);
|
||||
placeholder.put("shopId", null);
|
||||
placeholder.put("commission", null);
|
||||
placeholder.put("commissionShare", null);
|
||||
placeholder.put("commissionInfo", null);
|
||||
placeholder.put("materialUrl", url);
|
||||
placeholder.put("images", Collections.emptyList());
|
||||
placeholder.put("wenan", Collections.emptyList());
|
||||
placeholder.put("_raw", Collections.singletonMap("sourceUrl", url));
|
||||
placeholder.put("parseFailed", true);
|
||||
products.add(placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理剩余的SKUID
|
||||
// 额外处理文本中单独出现的SKUID(避免重复)
|
||||
for (String skuid : skuids) {
|
||||
if (!processedSkuids.contains(skuid)) {
|
||||
try {
|
||||
Map<String, Object> productInfo = queryProductInfo(skuid);
|
||||
Map<String, Object> productInfo = queryProductInfoWithRetry(skuid, 2, 200);
|
||||
if (productInfo != null) {
|
||||
products.add(productInfo);
|
||||
processedSkuids.add(skuid);
|
||||
} else {
|
||||
Map<String, Object> placeholder = new HashMap<>();
|
||||
placeholder.put("skuid", skuid);
|
||||
placeholder.put("productName", "未解析:" + skuid);
|
||||
placeholder.put("price", null);
|
||||
placeholder.put("productImage", null);
|
||||
placeholder.put("shopName", null);
|
||||
placeholder.put("shopId", null);
|
||||
placeholder.put("commission", null);
|
||||
placeholder.put("commissionShare", null);
|
||||
placeholder.put("commissionInfo", null);
|
||||
placeholder.put("materialUrl", null);
|
||||
placeholder.put("images", Collections.emptyList());
|
||||
placeholder.put("wenan", Collections.emptyList());
|
||||
placeholder.put("_raw", Collections.singletonMap("skuid", skuid));
|
||||
placeholder.put("parseFailed", true);
|
||||
products.add(placeholder);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("查询商品信息失败,SKUID: {}", skuid, e);
|
||||
log.error("查询商品信息失败(占位返回),SKUID: {}", skuid, e);
|
||||
Map<String, Object> placeholder = new HashMap<>();
|
||||
placeholder.put("skuid", skuid);
|
||||
placeholder.put("productName", "未解析:" + skuid);
|
||||
placeholder.put("price", null);
|
||||
placeholder.put("productImage", null);
|
||||
placeholder.put("shopName", null);
|
||||
placeholder.put("shopId", null);
|
||||
placeholder.put("commission", null);
|
||||
placeholder.put("commissionShare", null);
|
||||
placeholder.put("commissionInfo", null);
|
||||
placeholder.put("materialUrl", null);
|
||||
placeholder.put("images", Collections.emptyList());
|
||||
placeholder.put("wenan", Collections.emptyList());
|
||||
placeholder.put("_raw", Collections.singletonMap("skuid", skuid));
|
||||
placeholder.put("parseFailed", true);
|
||||
products.add(placeholder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,6 +310,32 @@ public class BatchPublishServiceImpl implements IBatchPublishService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 带重试的商品信息查询
|
||||
* @param urlOrSkuid 传入URL或SKUID
|
||||
* @param retries 重试次数
|
||||
* @param backoffMillis 两次尝试之间等待毫秒
|
||||
*/
|
||||
private Map<String, Object> queryProductInfoWithRetry(String urlOrSkuid, int retries, long backoffMillis) {
|
||||
int attempts = 0;
|
||||
while (attempts <= retries) {
|
||||
Map<String, Object> result = queryProductInfo(urlOrSkuid);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
attempts++;
|
||||
if (attempts <= retries) {
|
||||
try {
|
||||
Thread.sleep(backoffMillis);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化商品信息
|
||||
*/
|
||||
@@ -529,7 +610,9 @@ public class BatchPublishServiceImpl implements IBatchPublishService
|
||||
shop.setProvince(commonParams.getProvince());
|
||||
shop.setCity(commonParams.getCity());
|
||||
shop.setDistrict(commonParams.getDistrict());
|
||||
shop.setTitle(item.getProductName()); // 使用商品名称
|
||||
// 标题长度限制:最多60个字符,做安全截断(按code point防止截断表情)
|
||||
String title = item.getProductName();
|
||||
shop.setTitle(truncateByCodePoints(title, 60));
|
||||
|
||||
// 【修改】使用用户选择的文案
|
||||
String content = getSelectedWenanContent(productConfig);
|
||||
@@ -643,6 +726,24 @@ public class BatchPublishServiceImpl implements IBatchPublishService
|
||||
return wenanItem != null ? wenanItem.getContent() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全按字符数截断字符串(按Unicode code point,避免截断表情导致乱码)
|
||||
*/
|
||||
private String truncateByCodePoints(String text, int maxChars) {
|
||||
if (text == null) {
|
||||
return null;
|
||||
}
|
||||
if (maxChars <= 0) {
|
||||
return "";
|
||||
}
|
||||
final int length = text.codePointCount(0, text.length());
|
||||
if (length <= maxChars) {
|
||||
return text;
|
||||
}
|
||||
int endIndex = text.offsetByCodePoints(0, maxChars);
|
||||
return text.substring(0, endIndex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 延迟上架商品
|
||||
|
||||
Reference in New Issue
Block a user