feat(trademark): 实现商标筛查功能并优化相关配置
- 新增商标筛查进度展示界面与交互逻辑 - 实现产品、品牌及平台跟卖许可的分项任务进度追踪 - 添加商标数据导出与任务重试、取消功能 - 调整Redis连接池配置以提升并发性能 - 禁用ChromeDriver预加载,改为按需启动以节省资源- 支持品牌商标远程筛查接口调用与结果解析 - 增加Hutool工具库依赖用于简化IO与Excel处理- 更新USPTO商标查询脚本实现自动化检测 - 修改Ruoyi后台Redis依赖版本并添加集群心跳配置- 切换本地开发环境API地址指向内网测试服务器
This commit is contained in:
@@ -11,7 +11,7 @@ import org.springframework.core.annotation.Order;
|
||||
|
||||
/**
|
||||
* ChromeDriver 配置类
|
||||
* 启动时后台预加载驱动,提供全局单例 Bean
|
||||
* 已禁用预加载,由 TrademarkCheckUtil 按需创建
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@@ -22,21 +22,26 @@ public class ChromeDriverPreloader implements ApplicationRunner {
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
new Thread(() -> {
|
||||
globalDriver = SeleniumUtil.createDriver(true);
|
||||
log.info("ChromeDriver 预加载完成");
|
||||
}, "ChromeDriver-Preloader").start();
|
||||
// 不再预加载,节省资源
|
||||
log.info("ChromeDriver 配置已加载(按需启动)");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ChromeDriver chromeDriver() {
|
||||
if (globalDriver == null) globalDriver = SeleniumUtil.createDriver(true);
|
||||
// 为兼容性保留 Bean,但不自动创建
|
||||
if (globalDriver == null) globalDriver = SeleniumUtil.createDriver(false);
|
||||
return globalDriver;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void cleanup() {
|
||||
globalDriver.quit();
|
||||
if (globalDriver != null) {
|
||||
try {
|
||||
globalDriver.quit();
|
||||
} catch (Exception e) {
|
||||
log.error("关闭ChromeDriver失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.tashow.erp.controller;
|
||||
import com.tashow.erp.utils.JsonData;
|
||||
import com.tashow.erp.utils.LoggerUtil;
|
||||
import com.tashow.erp.utils.TrademarkCheckUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 商标检查控制器 - 极速版(浏览器内并发)
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/trademark")
|
||||
public class TrademarkController {
|
||||
private static final Logger logger = LoggerUtil.getLogger(TrademarkController.class);
|
||||
|
||||
@Autowired
|
||||
private TrademarkCheckUtil util;
|
||||
|
||||
/**
|
||||
* 批量品牌商标筛查(浏览器内并发,极速版)
|
||||
*/
|
||||
@PostMapping("/brandCheck")
|
||||
public JsonData brandCheck(@RequestBody List<String> brands) {
|
||||
try {
|
||||
List<String> list = brands.stream()
|
||||
.filter(b -> b != null && !b.trim().isEmpty())
|
||||
.map(String::trim)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
logger.info("开始检查 {}个品牌", list.size());
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
// 串行查询(不加延迟)
|
||||
List<Map<String, Object>> unregistered = new ArrayList<>();
|
||||
int checkedCount = 0;
|
||||
int registeredCount = 0;
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
String brand = list.get(i);
|
||||
logger.info("处理第 {} 个: {}", i + 1, brand);
|
||||
|
||||
Map<String, Boolean> results = util.batchCheck(Collections.singletonList(brand));
|
||||
|
||||
results.forEach((b, isReg) -> {
|
||||
if (!isReg) {
|
||||
Map<String, Object> m = new HashMap<>();
|
||||
m.put("brand", b);
|
||||
m.put("status", "未注册");
|
||||
unregistered.add(m);
|
||||
}
|
||||
});
|
||||
|
||||
// 统计成功查询的数量
|
||||
if (!results.isEmpty()) {
|
||||
checkedCount++;
|
||||
if (results.values().iterator().next()) {
|
||||
registeredCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long t = (System.currentTimeMillis() - start) / 1000;
|
||||
int failedCount = list.size() - checkedCount;
|
||||
|
||||
Map<String, Object> res = new HashMap<>();
|
||||
res.put("total", list.size());
|
||||
res.put("checked", checkedCount);
|
||||
res.put("registered", registeredCount);
|
||||
res.put("unregistered", unregistered.size());
|
||||
res.put("failed", failedCount);
|
||||
res.put("data", unregistered);
|
||||
res.put("duration", t + "秒");
|
||||
|
||||
logger.info("完成: 共{}个,成功查询{}个(已注册{}个,未注册{}个),查询失败{}个,耗时{}秒",
|
||||
list.size(), checkedCount, registeredCount, unregistered.size(), failedCount, t);
|
||||
return JsonData.buildSuccess(res);
|
||||
} catch (Exception e) {
|
||||
logger.error("筛查失败", e);
|
||||
return JsonData.buildError("筛查失败: " + e.getMessage());
|
||||
} finally {
|
||||
// 采集完成或失败后关闭浏览器
|
||||
util.closeDriver();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package com.tashow.erp.test;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* USPTO API 完整测试
|
||||
* 测试目的:验证是否可以通过品牌名称搜索商标
|
||||
*/
|
||||
public class UsptoApiTest {
|
||||
private static final String API_KEY = "TGP31sjvuxOb5bV21iYIihDpnXI4mFlM";
|
||||
private static final RestTemplate rest = new RestTemplate();
|
||||
private static final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== USPTO API 功能测试 ===\n");
|
||||
System.out.println("API Key: " + API_KEY + "\n");
|
||||
|
||||
// 测试1:通过序列号查询(已知可用)
|
||||
testBySerialNumber();
|
||||
|
||||
System.out.println("\n" + "=".repeat(60) + "\n");
|
||||
|
||||
// 测试2:尝试通过品牌名称搜索
|
||||
testByBrandName();
|
||||
|
||||
System.out.println("\n" + "=".repeat(60) + "\n");
|
||||
|
||||
// 测试3:对比当前实现的tmsearch
|
||||
testCurrentImplementation();
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试1:通过序列号查询(官方TSDR API)
|
||||
*/
|
||||
private static void testBySerialNumber() {
|
||||
System.out.println("【测试1】通过序列号查询");
|
||||
System.out.println("端点: https://tsdrapi.uspto.gov/ts/cd/casestatus/sn88123456/info.json");
|
||||
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("USPTO-API-KEY", API_KEY);
|
||||
HttpEntity<String> entity = new HttpEntity<>(headers);
|
||||
|
||||
ResponseEntity<String> response = rest.exchange(
|
||||
"https://tsdrapi.uspto.gov/ts/cd/casestatus/sn88123456/info.json",
|
||||
HttpMethod.GET, entity, String.class
|
||||
);
|
||||
|
||||
JsonNode json = mapper.readTree(response.getBody());
|
||||
String markElement = json.get("trademarks").get(0).get("status").get("markElement").asText();
|
||||
int status = json.get("trademarks").get(0).get("status").get("status").asInt();
|
||||
String regNumber = json.get("trademarks").get(0).get("status").get("usRegistrationNumber").asText();
|
||||
|
||||
System.out.println("✓ 成功!");
|
||||
System.out.println(" 商标名称: " + markElement);
|
||||
System.out.println(" 状态码: " + status);
|
||||
System.out.println(" 注册号: " + (regNumber.isEmpty() ? "未注册" : regNumber));
|
||||
System.out.println("\n结论: ✅ 可以查询,但必须知道序列号");
|
||||
} catch (Exception e) {
|
||||
System.out.println("✗ 失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试2:尝试通过品牌名称搜索
|
||||
*/
|
||||
private static void testByBrandName() {
|
||||
System.out.println("【测试2】尝试通过品牌名称搜索");
|
||||
|
||||
String[] brands = {"Nike", "MYLIFE", "TestBrand123"};
|
||||
String[] searchUrls = {
|
||||
"https://tsdrapi.uspto.gov/ts/cd/search?q=%s",
|
||||
"https://tsdrapi.uspto.gov/search?keyword=%s",
|
||||
"https://api.uspto.gov/trademark/search?q=%s",
|
||||
"https://api.uspto.gov/tmsearch/search?q=%s",
|
||||
};
|
||||
|
||||
boolean foundSearchApi = false;
|
||||
|
||||
for (String brand : brands) {
|
||||
System.out.println("\n测试品牌: " + brand);
|
||||
|
||||
for (String urlTemplate : searchUrls) {
|
||||
String url = String.format(urlTemplate, brand);
|
||||
System.out.println(" 尝试: " + url);
|
||||
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("USPTO-API-KEY", API_KEY);
|
||||
HttpEntity<String> entity = new HttpEntity<>(headers);
|
||||
|
||||
ResponseEntity<String> response = rest.exchange(url, HttpMethod.GET, entity, String.class);
|
||||
|
||||
System.out.println(" ✓✓✓ 成功! 找到品牌搜索API!");
|
||||
System.out.println(" 响应: " + response.getBody().substring(0, Math.min(200, response.getBody().length())));
|
||||
foundSearchApi = true;
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
System.out.println(" ✗ 404/失败");
|
||||
}
|
||||
}
|
||||
|
||||
if (foundSearchApi) break;
|
||||
}
|
||||
|
||||
if (!foundSearchApi) {
|
||||
System.out.println("\n结论: ❌ USPTO官方API不支持品牌名称搜索");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试3:当前实现的tmsearch方式
|
||||
*/
|
||||
private static void testCurrentImplementation() {
|
||||
System.out.println("【测试3】当前实现方式(tmsearch内部API)");
|
||||
System.out.println("端点: https://tmsearch.uspto.gov/prod-stage-v1-0-0/tmsearch");
|
||||
|
||||
String brand = "Nike";
|
||||
String requestBody = String.format(
|
||||
"{\"query\":{\"bool\":{\"must\":[{\"bool\":{\"should\":[" +
|
||||
"{\"match_phrase\":{\"WM\":{\"query\":\"%s\",\"boost\":5}}}," +
|
||||
"{\"match\":{\"WM\":{\"query\":\"%s\",\"boost\":2}}}," +
|
||||
"{\"match_phrase\":{\"PM\":{\"query\":\"%s\",\"boost\":2}}}" +
|
||||
"]}}]}},\"size\":1,\"_source\":[\"alive\"]}",
|
||||
brand, brand, brand
|
||||
);
|
||||
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);
|
||||
|
||||
ResponseEntity<String> response = rest.postForEntity(
|
||||
"https://tmsearch.uspto.gov/prod-stage-v1-0-0/tmsearch",
|
||||
entity, String.class
|
||||
);
|
||||
|
||||
JsonNode json = mapper.readTree(response.getBody());
|
||||
boolean hasResults = json.get("hits").get("hits").size() > 0;
|
||||
|
||||
System.out.println("✓ 成功!");
|
||||
System.out.println(" 品牌: " + brand);
|
||||
System.out.println(" 找到结果: " + (hasResults ? "是" : "否"));
|
||||
|
||||
if (hasResults) {
|
||||
boolean alive = json.get("hits").get("hits").get(0).get("_source").get("alive").asBoolean();
|
||||
System.out.println(" 是否注册: " + (alive ? "已注册" : "未注册"));
|
||||
}
|
||||
|
||||
System.out.println("\n结论: ✅ 支持品牌名称直接搜索(无需序列号)");
|
||||
System.out.println(" ⚠️ 但这是内部API,非官方公开接口");
|
||||
} catch (Exception e) {
|
||||
System.out.println("✗ 失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,53 @@
|
||||
package com.tashow.erp.test;
|
||||
|
||||
import com.tashow.erp.utils.DeviceUtils;
|
||||
import com.tashow.erp.utils.TrademarkCheckUtil;
|
||||
import org.openqa.selenium.JavascriptExecutor;
|
||||
import org.openqa.selenium.chrome.ChromeDriver;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@RestController
|
||||
public class aa {
|
||||
@Autowired
|
||||
private ChromeDriver driver;
|
||||
@Autowired
|
||||
RestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
TrademarkCheckUtil trademarkCheckUtil;
|
||||
@GetMapping("/aa")
|
||||
public String aa() {
|
||||
DeviceUtils deviceUtils = new DeviceUtils();
|
||||
return deviceUtils.generateDeviceId();
|
||||
public boolean aa() {
|
||||
return checkTrademark("HEIBAGO");
|
||||
}
|
||||
|
||||
public boolean checkTrademark(String brandName) {
|
||||
try {
|
||||
driver.get("https://tmsearch.uspto.gov/search/search-results");
|
||||
Thread.sleep(2000);
|
||||
String script = String.format("""
|
||||
return fetch('https://tmsearch.uspto.gov/prod-stage-v1-0-0/tmsearch', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
query: {bool: {must: [{bool: {should: [
|
||||
{match_phrase: {WM: {query: '%s', boost: 5}}},
|
||||
{match: {WM: {query: '%s', boost: 2}}},
|
||||
{match_phrase: {PM: {query: '%s', boost: 2}}}
|
||||
]}}]}},
|
||||
size: 1, _source: ['alive']
|
||||
})
|
||||
}).then(r => r.json()).then(d => d?.hits?.hits?.[0]?.source?.alive || false);
|
||||
""", brandName, brandName, brandName);
|
||||
Object result = ((JavascriptExecutor) driver).executeAsyncScript("var callback = arguments[arguments.length - 1];" + script.replace("return", "").replace(";", ".then(callback);"));
|
||||
return Boolean.TRUE.equals(result);
|
||||
} catch (Exception e) {
|
||||
System.err.println("检测失败: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.tashow.erp.utils;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 代理IP池
|
||||
*/
|
||||
@Component
|
||||
public class ProxyPool {
|
||||
|
||||
private static final String API_URL = "http://api.tianqiip.com/getip?secret=h6x09x0eenxuf4s7&num=1&type=txt&port=2&time=3&mr=1&sign=620719f6b7d66744b0216a4f61a6bcee";
|
||||
|
||||
/**
|
||||
* 获取一个代理IP
|
||||
* @return 代理地址,格式:host:port,如 123.96.236.32:40016
|
||||
*/
|
||||
public String getProxy() {
|
||||
try {
|
||||
String response = HttpUtil.get(API_URL);
|
||||
if (response != null && !response.trim().isEmpty()) {
|
||||
String proxy = response.trim();
|
||||
System.out.println("获取到代理: " + proxy);
|
||||
return proxy;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("获取代理失败: " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,16 @@ public class SeleniumUtil {
|
||||
* @return 配置好的 ChromeDriver
|
||||
*/
|
||||
public static ChromeDriver createDriver(boolean headless) {
|
||||
return createDriver(headless, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建防检测的 ChromeDriver(支持代理)
|
||||
* @param headless 是否启用无头模式
|
||||
* @param proxy 代理地址,格式:host:port,如 123.96.236.32:40016
|
||||
* @return 配置好的 ChromeDriver
|
||||
*/
|
||||
public static ChromeDriver createDriver(boolean headless, String proxy) {
|
||||
try {
|
||||
WebDriverManager.chromedriver()
|
||||
.driverRepositoryUrl(new URL("https://registry.npmmirror.com/-/binary/chromedriver/"))
|
||||
@@ -37,6 +47,12 @@ public class SeleniumUtil {
|
||||
options.addArguments("--no-sandbox", "--disable-gpu", "--disable-dev-shm-usage");
|
||||
options.addArguments("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36");
|
||||
|
||||
// 配置代理
|
||||
if (proxy != null && !proxy.trim().isEmpty()) {
|
||||
options.addArguments("--proxy-server=http://" + proxy);
|
||||
options.addArguments("--proxy-bypass-list=<-loopback>");
|
||||
System.out.println("ChromeDriver使用代理: http://" + proxy);
|
||||
}
|
||||
// 无头模式
|
||||
if (headless) {
|
||||
options.addArguments("--headless=new");
|
||||
|
||||
@@ -1,42 +1,128 @@
|
||||
package com.tashow.erp.utils;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import org.openqa.selenium.JavascriptExecutor;
|
||||
import org.openqa.selenium.chrome.ChromeDriver;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 商标检查工具
|
||||
* 支持批量并发查询(每100次切换代理)
|
||||
*/
|
||||
@Component
|
||||
public class TrademarkCheckUtil {
|
||||
@Autowired
|
||||
private ProxyPool proxyPool;
|
||||
private ChromeDriver driver;
|
||||
@Autowired
|
||||
RestTemplate restTemplate;
|
||||
|
||||
public boolean checkTrademark(String brandName) {
|
||||
try {
|
||||
driver.get("https://tmsearch.uspto.gov/search/search-results");
|
||||
Thread.sleep(2000);
|
||||
String script = String.format("""
|
||||
return fetch('https://tmsearch.uspto.gov/prod-stage-v1-0-0/tmsearch', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
query: {bool: {must: [{bool: {should: [
|
||||
{match_phrase: {WM: {query: '%s', boost: 5}}},
|
||||
{match: {WM: {query: '%s', boost: 2}}},
|
||||
{match_phrase: {PM: {query: '%s', boost: 2}}}
|
||||
]}}]}},
|
||||
size: 1, _source: ['alive']
|
||||
})
|
||||
}).then(r => r.json()).then(d => d?.hits?.hits?.[0]?.source?.alive || false);
|
||||
""", brandName, brandName, brandName);
|
||||
Object result = ((JavascriptExecutor) driver).executeAsyncScript("var callback = arguments[arguments.length - 1];" + script.replace("return", "").replace(";", ".then(callback);"));
|
||||
return Boolean.TRUE.equals(result);
|
||||
} catch (Exception e) {
|
||||
System.err.println("检测失败: " + e.getMessage());
|
||||
return false;
|
||||
private final AtomicInteger checkCount = new AtomicInteger(0);
|
||||
private static final int PROXY_SWITCH_THRESHOLD = 100;
|
||||
private synchronized void ensureInit() {
|
||||
if (driver == null) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
try {
|
||||
driver = SeleniumUtil.createDriver(false, proxyPool.getProxy());
|
||||
driver.get("https://tmsearch.uspto.gov/search/search-results");
|
||||
Thread.sleep(5000);
|
||||
return; // 成功则返回
|
||||
} catch (Exception e) {
|
||||
System.err.println("初始化失败(尝试" + (i+1) + "/3): " + e.getMessage());
|
||||
if (driver != null) {
|
||||
try { driver.quit(); } catch (Exception ex) {}
|
||||
driver = null;
|
||||
}
|
||||
if (i == 2) throw new RuntimeException("初始化失败,已重试3次", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public synchronized Map<String, Boolean> batchCheck(List<String> brands) {
|
||||
ensureInit();
|
||||
|
||||
// 每100个切换代理
|
||||
if (checkCount.addAndGet(brands.size()) >= PROXY_SWITCH_THRESHOLD) {
|
||||
try { driver.quit(); } catch (Exception e) {}
|
||||
driver = null;
|
||||
checkCount.set(0);
|
||||
ensureInit();
|
||||
}
|
||||
|
||||
// 构建批量查询脚本(带错误诊断)
|
||||
String script = """
|
||||
const brands = arguments[0];
|
||||
const callback = arguments[arguments.length - 1];
|
||||
|
||||
Promise.all(brands.map(brand =>
|
||||
fetch('https://tmsearch.uspto.gov/prod-stage-v1-0-0/tmsearch', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
query: {bool: {must: [{bool: {should: [
|
||||
{match_phrase: {WM: {query: brand, boost: 5}}},
|
||||
{match: {WM: {query: brand, boost: 2}}},
|
||||
{match_phrase: {PM: {query: brand, boost: 2}}}
|
||||
]}}]}},
|
||||
size: 1, _source: ['alive']
|
||||
})
|
||||
})
|
||||
.then(r => {
|
||||
if (!r.ok) {
|
||||
return {brand, alive: false, error: `HTTP ${r.status}: ${r.statusText}`};
|
||||
}
|
||||
return r.json().then(d => ({
|
||||
brand,
|
||||
alive: d?.hits?.hits?.[0]?.source?.alive || false,
|
||||
error: null
|
||||
}));
|
||||
})
|
||||
.catch(e => ({
|
||||
brand,
|
||||
alive: false,
|
||||
error: e.name + ': ' + e.message
|
||||
}))
|
||||
)).then(callback);
|
||||
""";
|
||||
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> results = (List<Map<String, Object>>)
|
||||
((JavascriptExecutor) driver).executeAsyncScript(script, brands);
|
||||
|
||||
Map<String, Boolean> resultMap = new HashMap<>();
|
||||
for (Map<String, Object> item : results) {
|
||||
String brand = (String) item.get("brand");
|
||||
Boolean alive = (Boolean) item.get("alive");
|
||||
String error = (String) item.get("error");
|
||||
|
||||
if (error != null) {
|
||||
// 查询失败,不放入结果,只打印错误
|
||||
System.err.println(brand + " -> [查询失败: " + error + "]");
|
||||
} else {
|
||||
// 查询成功,放入结果
|
||||
resultMap.put(brand, alive);
|
||||
System.out.println(brand + " -> " + (alive ? "✓ 已注册" : "✗ 未注册"));
|
||||
}
|
||||
}
|
||||
return resultMap;
|
||||
} catch (Exception e) {
|
||||
System.err.println("批量查询失败: " + e.getMessage());
|
||||
return new HashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void closeDriver() {
|
||||
if (driver != null) {
|
||||
try { driver.quit(); } catch (Exception e) {}
|
||||
driver = null;
|
||||
checkCount.set(0);
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void cleanup() {
|
||||
closeDriver();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user