From 5b48727fb21cc7fc8d30508e52051aa1151bea44 Mon Sep 17 00:00:00 2001 From: Leo Date: Mon, 10 Nov 2025 18:43:27 +0800 Subject: [PATCH] 1 --- .../van/business/util/JDProductService.java | 117 ++++++++++++++++-- 1 file changed, 105 insertions(+), 12 deletions(-) diff --git a/src/main/java/cn/van/business/util/JDProductService.java b/src/main/java/cn/van/business/util/JDProductService.java index a9c0ef1..e7542ba 100644 --- a/src/main/java/cn/van/business/util/JDProductService.java +++ b/src/main/java/cn/van/business/util/JDProductService.java @@ -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 == '.'; + } }