diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/JDOrderController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/JDOrderController.java index 16e49b6..89853a8 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/JDOrderController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/JDOrderController.java @@ -37,6 +37,15 @@ public class JDOrderController extends BaseController { private final IJDOrderService jdOrderService; private final IOrderRowsService orderRowsService; private final IGiftCouponService giftCouponService; + private static final java.util.regex.Pattern URL_DETECT_PATTERN = java.util.regex.Pattern.compile( + "(https?://[^\\s]+)|(u\\.jd\\.com/[^\\s]+)", + java.util.regex.Pattern.CASE_INSENSITIVE); + private static final java.util.regex.Pattern UJD_LINK_PATTERN = java.util.regex.Pattern.compile( + "^https?://u\\.jd\\.com/[A-Za-z0-9]+[A-Za-z0-9_-]*$", + java.util.regex.Pattern.CASE_INSENSITIVE); + private static final java.util.regex.Pattern JINGFEN_LINK_PATTERN = java.util.regex.Pattern.compile( + "^https?://jingfen\\.jd\\.com/detail/[A-Za-z0-9]+\\.html$", + java.util.regex.Pattern.CASE_INSENSITIVE); public JDOrderController(IJDOrderService jdOrderService, IOrderRowsService orderRowsService, IGiftCouponService giftCouponService) { this.jdOrderService = jdOrderService; @@ -566,22 +575,20 @@ public class JDOrderController extends BaseController { content.length(), amount, quantity, owner); // 提取文本中的所有URL(京东链接) - java.util.List urls = new java.util.ArrayList<>(); - java.util.regex.Pattern urlPattern = java.util.regex.Pattern.compile( - "(https?://[^\\s]+)|(u\\.jd\\.com/[^\\s]+)", - java.util.regex.Pattern.CASE_INSENSITIVE); - java.util.regex.Matcher matcher = urlPattern.matcher(content); - + java.util.List urlSegments = new java.util.ArrayList<>(); + java.util.regex.Matcher matcher = URL_DETECT_PATTERN.matcher(content); + while (matcher.find()) { - String url = matcher.group(0); - if (url != null && !url.trim().isEmpty()) { - urls.add(url.trim()); + String segment = matcher.group(0); + UrlSegment urlInfo = parseUrlSegment(segment); + if (urlInfo != null) { + urlSegments.add(urlInfo); } } - logger.info("文本URL替换 - 提取到{}个URL", urls.size()); + logger.info("文本URL替换 - 提取到{}个URL", urlSegments.size()); - if (urls.isEmpty()) { + if (urlSegments.isEmpty()) { return AjaxResult.success(new JSONObject() .fluentPut("replacedContent", content) .fluentPut("originalContent", content) @@ -596,17 +603,29 @@ public class JDOrderController extends BaseController { JSONArray replacementInfo = new JSONArray(); int successCount = 0; - for (int i = 0; i < urls.size(); i++) { - String originalUrl = urls.get(i); - logger.info("处理第{}/{}个URL: {}", i + 1, urls.size(), originalUrl); + for (int i = 0; i < urlSegments.size(); i++) { + UrlSegment urlSegment = urlSegments.get(i); + logger.info("处理第{}/{}个URL: {}", i + 1, urlSegments.size(), urlSegment.urlPart); JSONObject product = null; // 在try外部声明,以便在catch中使用 try { + if (urlSegment.normalizedJdUrl == null) { + logger.warn("URL{}不是京东推广链接,跳过: {}", i + 1, urlSegment.original); + JSONObject info = new JSONObject(); + info.put("index", i + 1); + info.put("originalUrl", urlSegment.urlPart); + info.put("newUrl", urlSegment.urlPart); + info.put("success", false); + info.put("error", "非京东推广链接或格式不支持"); + replacementInfo.add(info); + continue; + } + // 1. 查询该URL的商品信息 String queryUrl = requestUrl + "generatePromotionContent"; JSONObject queryParam = new JSONObject(); queryParam.put("skey", skey); - queryParam.put("promotionContent", originalUrl); + queryParam.put("promotionContent", urlSegment.normalizedJdUrl); String queryResult = HttpUtils.sendJsonPost(queryUrl, queryParam.toJSONString()); logger.debug("商品查询响应: {}", queryResult); @@ -643,11 +662,11 @@ public class JDOrderController extends BaseController { errorMsg = apiError; } } - logger.warn("URL{}商品信息查询失败: {}", originalUrl, errorMsg); + logger.warn("URL{}商品信息查询失败: {}", urlSegment.urlPart, errorMsg); JSONObject info = new JSONObject(); info.put("index", i + 1); - info.put("originalUrl", originalUrl); - info.put("newUrl", originalUrl); + info.put("originalUrl", urlSegment.urlPart); + info.put("newUrl", urlSegment.urlPart); info.put("success", false); info.put("error", errorMsg); replacementInfo.add(info); @@ -704,8 +723,8 @@ public class JDOrderController extends BaseController { logger.warn("POP商品{}缺少有效的oriItemId/materialUrl/skuId", product.getString("skuName")); JSONObject info = new JSONObject(); info.put("index", i + 1); - info.put("originalUrl", originalUrl); - info.put("newUrl", originalUrl); + info.put("originalUrl", urlSegment.urlPart); + info.put("newUrl", urlSegment.urlPart); info.put("success", false); info.put("error", "POP商品信息不完整"); replacementInfo.add(info); @@ -723,8 +742,8 @@ public class JDOrderController extends BaseController { logger.warn("自营商品{}缺少有效的skuId/materialUrl", product.getString("skuName")); JSONObject info = new JSONObject(); info.put("index", i + 1); - info.put("originalUrl", originalUrl); - info.put("newUrl", originalUrl); + info.put("originalUrl", urlSegment.urlPart); + info.put("newUrl", urlSegment.urlPart); info.put("success", false); info.put("error", "自营商品信息不完整"); replacementInfo.add(info); @@ -749,11 +768,11 @@ public class JDOrderController extends BaseController { errorMsg = apiError; } } - logger.warn("URL{}礼金创建失败: {}", originalUrl, errorMsg); + logger.warn("URL{}礼金创建失败: {}", urlSegment.urlPart, errorMsg); JSONObject info = new JSONObject(); info.put("index", i + 1); - info.put("originalUrl", originalUrl); - info.put("newUrl", originalUrl); + info.put("originalUrl", urlSegment.urlPart); + info.put("newUrl", urlSegment.urlPart); info.put("success", false); info.put("error", errorMsg); if (product != null && product.getString("skuName") != null) { @@ -769,7 +788,7 @@ public class JDOrderController extends BaseController { String transferUrl = requestUrl + "transfer"; JSONObject transferParam = new JSONObject(); transferParam.put("skey", skey); - transferParam.put("materialUrl", originalUrl); + transferParam.put("materialUrl", urlSegment.normalizedJdUrl); transferParam.put("giftCouponKey", giftCouponKey); String transferResult = HttpUtils.sendJsonPost(transferUrl, transferParam.toJSONString()); @@ -789,11 +808,11 @@ public class JDOrderController extends BaseController { errorMsg = apiError; } } - logger.warn("URL{}转链失败: {}", originalUrl, errorMsg); + logger.warn("URL{}转链失败: {}", urlSegment.urlPart, errorMsg); JSONObject info = new JSONObject(); info.put("index", i + 1); - info.put("originalUrl", originalUrl); - info.put("newUrl", originalUrl); + info.put("originalUrl", urlSegment.urlPart); + info.put("newUrl", urlSegment.urlPart); info.put("success", false); info.put("giftCouponKey", giftCouponKey); info.put("error", errorMsg); @@ -807,11 +826,11 @@ public class JDOrderController extends BaseController { String shortURL = transferData.getString("shortURL"); // 4. 替换文本中的URL - replacedContent = replacedContent.replace(originalUrl, shortURL); + replacedContent = replaceUrlInContent(replacedContent, urlSegment, shortURL); JSONObject info = new JSONObject(); info.put("index", i + 1); - info.put("originalUrl", originalUrl); + info.put("originalUrl", urlSegment.urlPart); info.put("newUrl", shortURL); info.put("success", true); info.put("giftCouponKey", giftCouponKey); @@ -819,18 +838,18 @@ public class JDOrderController extends BaseController { replacementInfo.add(info); successCount++; - logger.info("URL{}处理成功: {} -> {}", i + 1, originalUrl, shortURL); + logger.info("URL{}处理成功: {} -> {}", i + 1, urlSegment.urlPart, shortURL); } catch (Exception e) { - logger.error("处理URL{}失败: {}", originalUrl, e.getMessage(), e); + logger.error("处理URL{}失败: {}", urlSegment.urlPart, e.getMessage(), e); String errorMsg = e.getMessage(); if (errorMsg == null || errorMsg.trim().isEmpty()) { errorMsg = "处理失败: " + e.getClass().getSimpleName(); } JSONObject info = new JSONObject(); info.put("index", i + 1); - info.put("originalUrl", originalUrl); - info.put("newUrl", originalUrl); + info.put("originalUrl", urlSegment.urlPart); + info.put("newUrl", urlSegment.urlPart); info.put("success", false); info.put("error", errorMsg); if (product != null && product.getString("skuName") != null) { @@ -840,13 +859,13 @@ public class JDOrderController extends BaseController { } } - logger.info("文本URL替换完成 - 成功{}/{}", successCount, urls.size()); + logger.info("文本URL替换完成 - 成功{}/{}", successCount, urlSegments.size()); JSONObject response = new JSONObject(); response.put("replacedContent", replacedContent); response.put("originalContent", content); response.put("replacements", replacementInfo); - response.put("totalUrls", urls.size()); + response.put("totalUrls", urlSegments.size()); response.put("replacedCount", successCount); return AjaxResult.success(response); @@ -1110,4 +1129,80 @@ public class JDOrderController extends BaseController { } return result.toString(); } + + private static class UrlSegment { + private final String original; + private final String urlPart; + private final String suffix; + private final String normalizedJdUrl; + + private UrlSegment(String original, String urlPart, String suffix, String normalizedJdUrl) { + this.original = original; + this.urlPart = urlPart; + this.suffix = suffix; + this.normalizedJdUrl = normalizedJdUrl; + } + } + + private static UrlSegment parseUrlSegment(String segment) { + if (segment == null || segment.trim().isEmpty()) { + return null; + } + String original = segment.trim(); + int urlLength = calculateUrlLength(original); + if (urlLength <= 0) { + return null; + } + String urlPart = original.substring(0, urlLength); + String suffix = original.substring(urlLength); + String normalized = normalizeJdUrl(urlPart); + return new UrlSegment(original, urlPart, suffix, normalized); + } + + private static int calculateUrlLength(String text) { + int length = 0; + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + if (isAllowedUrlChar(c)) { + length++; + } else { + break; + } + } + return length; + } + + private static boolean isAllowedUrlChar(char c) { + if (c <= 0x7F) { + return Character.isLetterOrDigit(c) || "-._~:/?#[]@!$&'()*+,;=%".indexOf(c) >= 0; + } + return false; + } + + private static String normalizeJdUrl(String urlPart) { + if (urlPart == null || urlPart.isEmpty()) { + return null; + } + String normalized = urlPart; + if (!normalized.startsWith("http://") && !normalized.startsWith("https://")) { + normalized = "https://" + normalized; + } + if (UJD_LINK_PATTERN.matcher(normalized).matches() || JINGFEN_LINK_PATTERN.matcher(normalized).matches()) { + return normalized; + } + return null; + } + + private static String replaceUrlInContent(String content, UrlSegment segment, String newUrl) { + if (content == null || segment == null || newUrl == null || newUrl.trim().isEmpty()) { + return content; + } + String suffix = segment.suffix != null ? segment.suffix : ""; + String replacementWithSuffix = newUrl + suffix; + String result = content.replace(segment.original, replacementWithSuffix); + if (result.equals(content) && !segment.original.equals(segment.urlPart)) { + result = result.replace(segment.urlPart, newUrl); + } + return result; + } }