This commit is contained in:
Leo
2025-11-10 18:43:27 +08:00
parent bb6c907cda
commit 5b48727fb2

View File

@@ -32,7 +32,7 @@ import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import static cn.van.business.util.JDUtil.*;
@@ -84,17 +84,29 @@ public class JDProductService {
for (String url : urls) {
try {
String format = dateFormat.format(new Date());
GoodsQueryResult productInfo = queryProductInfoByUJDUrl(url);
String originalUrlInText = url;
String normalizedUrl = normalizeJdUrl(originalUrlInText);
if (normalizedUrl == null) {
log.warn("检测到的链接无法识别为合法京东链接,跳过处理: {}", originalUrlInText);
JSONObject errorObj = new JSONObject();
errorObj.put("url", originalUrlInText);
errorObj.put("error", "链接格式不支持或识别失败");
resultArray.add(errorObj);
continue;
}
GoodsQueryResult productInfo = queryProductInfoByUJDUrl(normalizedUrl);
if (productInfo == null || productInfo.getCode() != 200 || productInfo.getData() == null || productInfo.getData().length == 0) {
JSONObject errorObj = new JSONObject();
errorObj.put("url", url);
errorObj.put("url", originalUrlInText);
errorObj.put("error", "链接查询失败");
resultArray.add(errorObj);
continue;
}
JSONObject productObj = new JSONObject();
productObj.put("originalUrl", url);
productObj.put("originalUrl", originalUrlInText);
productObj.put("normalizedUrl", normalizedUrl);
// 商品基本信息
productObj.put("materialUrl", productInfo.getData()[0].getMaterialUrl());
@@ -124,22 +136,24 @@ public class JDProductService {
// 生成转链后的短链
try {
String shortUrl = transfer(url, null);
String shortUrl = transfer(normalizedUrl, null);
String effectiveUrl = normalizedUrl;
if (shortUrl != null && !shortUrl.isEmpty()) {
productObj.put("shortUrl", shortUrl);
productObj.put("transferSuccess", true);
// 将短链替换原始链接,用于后续文案生成
url = shortUrl;
effectiveUrl = shortUrl;
} else {
productObj.put("shortUrl", url); // 如果转链失败,使用链接
productObj.put("shortUrl", normalizedUrl); // 如果转链失败,使用归一化后的链接
productObj.put("transferSuccess", false);
log.warn("转链失败,使用原链接: {}", url);
log.warn("转链失败,使用原链接: {}", normalizedUrl);
}
productObj.put("effectiveUrl", effectiveUrl);
} catch (Exception e) {
log.error("生成转链时发生异常: {}", url, e);
productObj.put("shortUrl", url); // 转链异常时使用原链接
log.error("生成转链时发生异常: {}", normalizedUrl, e);
productObj.put("shortUrl", normalizedUrl); // 转链异常时使用原链接
productObj.put("transferSuccess", false);
productObj.put("transferError", e.getMessage());
productObj.put("effectiveUrl", normalizedUrl);
}
// 文案信息
@@ -194,7 +208,12 @@ public class JDProductService {
JSONObject commonWenan = new JSONObject();
commonWenan.put("type", "通用文案");
// 将原始消息中的链接替换为转链后的短链
String messageWithShortUrl = message.replace(productObj.getString("originalUrl"), url);
String targetUrl = productObj.getString("effectiveUrl");
String normalizedForReplace = productObj.getString("normalizedUrl");
String messageWithShortUrl = message;
if (normalizedForReplace != null && targetUrl != null) {
messageWithShortUrl = message.replace(normalizedForReplace, targetUrl);
}
commonWenan.put("content", format + FANAN_COMMON + messageWithShortUrl);
wenanArray.add(commonWenan);
@@ -547,4 +566,78 @@ public class JDProductService {
log.info("批量创建礼金券完成 - 总数={}, 成功={}, 失败={}", batchSize, successCount, failCount);
return results;
}
private static final Pattern UJD_LINK_PATTERN = Pattern.compile("^https?://u\\.jd\\.com/[A-Za-z0-9]+[A-Za-z0-9_-]*$", Pattern.CASE_INSENSITIVE);
private static final Pattern JINGFEN_LINK_PATTERN = Pattern.compile("^https?://jingfen\\.jd\\.com/detail/[A-Za-z0-9]+\\.html$", Pattern.CASE_INSENSITIVE);
private static final Pattern TRAILING_SYMBOLS_PATTERN = Pattern.compile("[))】>》。,;;!!??“”\"'、…—\\s]+$");
private static String normalizeJdUrl(String rawUrl) {
if (rawUrl == null || rawUrl.trim().isEmpty()) {
return null;
}
String trimmed = rawUrl.trim();
// 截断常见中文/英文括号后的内容
int cutoffIndex = findCutoffIndex(trimmed);
if (cutoffIndex > -1) {
trimmed = trimmed.substring(0, cutoffIndex);
}
// 去掉末尾的标点符号
trimmed = TRAILING_SYMBOLS_PATTERN.matcher(trimmed).replaceAll("");
if (trimmed.isEmpty()) {
return null;
}
if (!trimmed.startsWith("http://") && !trimmed.startsWith("https://")) {
trimmed = "https://" + trimmed;
}
if (UJD_LINK_PATTERN.matcher(trimmed).matches() || JINGFEN_LINK_PATTERN.matcher(trimmed).matches()) {
return trimmed;
}
// 针对 u.jd.com 链接,尝试进一步截断到第一个不合法字符
if (trimmed.contains("u.jd.com/")) {
int schemeEnd = trimmed.indexOf("u.jd.com/") + "u.jd.com/".length();
StringBuilder sb = new StringBuilder(trimmed.substring(0, schemeEnd));
for (int i = schemeEnd; i < trimmed.length(); i++) {
char c = trimmed.charAt(i);
if (isAllowedShortLinkChar(c)) {
sb.append(c);
} else {
break;
}
}
String candidate = sb.toString();
if (UJD_LINK_PATTERN.matcher(candidate).matches()) {
return candidate;
}
}
return null;
}
private static int findCutoffIndex(String text) {
char[] stopChars = new char[]{'', '(', '[', '【', '<', '《', '「', '『'};
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (Character.isWhitespace(c)) {
return i;
}
for (char stopChar : stopChars) {
if (c == stopChar) {
return i;
}
}
}
return -1;
}
private static boolean isAllowedShortLinkChar(char c) {
return (c >= 'A' && c <= 'Z')
|| (c >= 'a' && c <= 'z')
|| (c >= '0' && c <= '9')
|| c == '-' || c == '_' || c == '.';
}
}