refactor(utils):优化商标查询重试机制- 将初始化失败重试次数从3次增加到5次

- 改进批量查询逻辑,支持单个品牌独立重试- 添加针对HTTP 403、网络错误等异常的代理切换机制-优化查询脚本构造方式,提高执行稳定性
- 增强错误处理和日志输出信息
- 移除控制器中冗余的注释描述
This commit is contained in:
2025-11-10 11:21:04 +08:00
parent c2e1617a99
commit 92ab782943
2 changed files with 88 additions and 98 deletions

View File

@@ -21,7 +21,7 @@ import java.time.LocalDateTime;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* 商标检查控制器 - 极速版(浏览器内并发) * 商标检查控制器
*/ */
@RestController @RestController
@RequestMapping("/api/trademark") @RequestMapping("/api/trademark")
@@ -63,12 +63,9 @@ public class TrademarkController {
.filter(b -> b != null && !b.trim().isEmpty()) .filter(b -> b != null && !b.trim().isEmpty())
.map(String::trim) .map(String::trim)
.collect(Collectors.toList()); .collect(Collectors.toList());
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
// 1. 先从全局缓存获取 // 1. 先从全局缓存获取
Map<String, Boolean> cached = cacheService.getCached(list); Map<String, Boolean> cached = cacheService.getCached(list);
// 2. 找出缓存未命中的品牌 // 2. 找出缓存未命中的品牌
List<String> toQuery = list.stream() List<String> toQuery = list.stream()
.filter(b -> !cached.containsKey(b)) .filter(b -> !cached.containsKey(b))

View File

@@ -28,7 +28,7 @@ public class TrademarkCheckUtil {
Thread.sleep(6000); Thread.sleep(6000);
return; // 成功则返回 return; // 成功则返回
} catch (Exception e) { } catch (Exception e) {
System.err.println("初始化失败(尝试" + (i+1) + "/3: " + e.getMessage()); System.err.println("初始化失败(尝试" + (i+1) + "/5: " + e.getMessage());
if (driver != null) { if (driver != null) {
try { driver.quit(); } catch (Exception ex) {} try { driver.quit(); } catch (Exception ex) {}
driver = null; driver = null;
@@ -40,106 +40,99 @@ public class TrademarkCheckUtil {
} }
public synchronized Map<String, Boolean> batchCheck(List<String> brands, Map<String, Boolean> alreadyQueried) { public synchronized Map<String, Boolean> batchCheck(List<String> brands, Map<String, Boolean> alreadyQueried) {
ensureInit(); Map<String, Boolean> resultMap = new HashMap<>();
int maxRetries = 5;
// 构建批量查询脚本(带错误诊断)
String script = """ for (String brand : brands) {
const brands = arguments[0]; int retryCount = 0;
const callback = arguments[arguments.length - 1]; boolean success = false;
Promise.all(brands.map(brand => while (retryCount < maxRetries && !success) {
fetch('https://tmsearch.uspto.gov/prod-stage-v1-0-0/tmsearch', { try {
method: 'POST', ensureInit();
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ String script = "const brands = ['" + brand.replace("'", "\\'") + "'];\n" +
query: {bool: {must: [{bool: {should: [ "const callback = arguments[arguments.length - 1];\n" +
{match_phrase: {WM: {query: brand, boost: 5}}}, "Promise.all(brands.map(b => \n" +
{match: {WM: {query: brand, boost: 2}}}, " fetch('https://tmsearch.uspto.gov/prod-stage-v1-0-0/tmsearch', {\n" +
{match_phrase: {PM: {query: brand, boost: 2}}} " method: 'POST',\n" +
]}}]}}, " headers: {'Content-Type': 'application/json'},\n" +
size: 1, _source: ['alive'] " body: JSON.stringify({\n" +
}) " query: {bool: {must: [{bool: {should: [\n" +
}) " {match_phrase: {WM: {query: b, boost: 5}}},\n" +
.then(r => { " {match: {WM: {query: b, boost: 2}}},\n" +
if (!r.ok) { " {match_phrase: {PM: {query: b, boost: 2}}}\n" +
return {brand, alive: false, error: `HTTP ${r.status}: ${r.statusText}`}; " ]}}]}},\n" +
} " size: 1, _source: ['alive']\n" +
return r.json().then(d => ({ " })\n" +
brand, " })\n" +
alive: d?.hits?.hits?.[0]?.source?.alive || false, " .then(r => {\n" +
error: null " if (!r.ok) {\n" +
})); " return {brand: b, alive: false, error: 'HTTP ' + r.status + ': ' + r.statusText};\n" +
}) " }\n" +
.catch(e => ({ " return r.json().then(d => ({\n" +
brand, " brand: b,\n" +
alive: false, " alive: d?.hits?.hits?.[0]?.source?.alive || false,\n" +
error: e.name + ': ' + e.message " error: null\n" +
})) " }));\n" +
)).then(callback); " })\n" +
"""; " .catch(e => ({\n" +
" brand: b,\n" +
try { " alive: false,\n" +
@SuppressWarnings("unchecked") " error: e.name + ': ' + e.message\n" +
List<Map<String, Object>> results = (List<Map<String, Object>>) " }))\n" +
((JavascriptExecutor) driver).executeAsyncScript(script, brands); ")).then(callback);";
// 检测是否有网络错误包括403、Failed to fetch等 @SuppressWarnings("unchecked")
boolean hasNetworkError = results.stream() List<Map<String, Object>> results = (List<Map<String, Object>>)
.anyMatch(item -> { ((JavascriptExecutor) driver).executeAsyncScript(script);
Map<String, Object> item = results.get(0);
String error = (String) item.get("error"); String error = (String) item.get("error");
return error != null && (
error.contains("HTTP 403") || if (error != null && (
error.contains("HTTP 403") ||
error.contains("Failed to fetch") || error.contains("Failed to fetch") ||
error.contains("NetworkError") || error.contains("NetworkError") ||
error.contains("TypeError") error.contains("TypeError") ||
); error.contains("script timeout"))) {
});
System.err.println(brand + " 查询失败(" + (retryCount + 1) + "/" + maxRetries + "): " + error + ",切换代理...");
// 如果有网络错误,切换代理并重试
if (hasNetworkError) { // 切换代理
System.err.println("检测到网络错误,切换代理并重试..."); try { driver.quit(); } catch (Exception e) {}
driver = null;
// 切换代理前保存已查询的品牌 retryCount++;
if (alreadyQueried != null && !alreadyQueried.isEmpty()) { continue;
try {
cacheService.saveResults(alreadyQueried);
System.out.println("代理切换,已保存 " + alreadyQueried.size() + " 个品牌到缓存");
} catch (Exception e) {
System.err.println("保存缓存失败: " + e.getMessage());
} }
}
// 成功或非网络错误
try { driver.quit(); } catch (Exception e) {} if (error == null) {
driver = null; Boolean alive = (Boolean) item.get("alive");
ensureInit(); resultMap.put(brand, alive);
System.out.println(brand + " -> " + (alive ? "✓ 已注册" : "✗ 未注册"));
// 重新执行查询 success = true;
@SuppressWarnings("unchecked") } else {
List<Map<String, Object>> retryResults = (List<Map<String, Object>>) System.err.println(brand + " -> [查询失败: " + error + "]");
((JavascriptExecutor) driver).executeAsyncScript(script, brands); resultMap.put(brand, false); // 失败也记录为未注册
results = retryResults; success = true;
} }
Map<String, Boolean> resultMap = new HashMap<>(); } catch (Exception e) {
for (Map<String, Object> item : results) { System.err.println(brand + " 查询异常(" + (retryCount + 1) + "/" + maxRetries + "): " + e.getMessage());
String brand = (String) item.get("brand"); try { driver.quit(); } catch (Exception ex) {}
Boolean alive = (Boolean) item.get("alive"); driver = null;
String error = (String) item.get("error"); retryCount++;
if (error != null) {
// 查询失败,不放入结果,只打印错误
System.err.println(brand + " -> [查询失败: " + error + "]");
} else {
// 查询成功,放入结果
resultMap.put(brand, alive);
System.out.println(brand + " -> " + (alive ? "✓ 已注册" : "✗ 未注册"));
} }
} }
return resultMap;
} catch (Exception e) { if (!success) {
System.err.println("批量查询失败: " + e.getMessage()); System.err.println(brand + " -> [查询失败: 已重试" + maxRetries + "次]");
return new HashMap<>(); resultMap.put(brand, false); // 失败也记录为未注册
}
} }
return resultMap;
} }
public synchronized void closeDriver() { public synchronized void closeDriver() {