feat(trademark): 实现商标筛查功能并优化相关配置

- 新增商标筛查进度展示界面与交互逻辑
- 实现产品、品牌及平台跟卖许可的分项任务进度追踪
- 添加商标数据导出与任务重试、取消功能
- 调整Redis连接池配置以提升并发性能
- 禁用ChromeDriver预加载,改为按需启动以节省资源- 支持品牌商标远程筛查接口调用与结果解析
- 增加Hutool工具库依赖用于简化IO与Excel处理- 更新USPTO商标查询脚本实现自动化检测
- 修改Ruoyi后台Redis依赖版本并添加集群心跳配置- 切换本地开发环境API地址指向内网测试服务器
This commit is contained in:
2025-11-04 15:39:15 +08:00
parent c9874f1786
commit a62d7b6147
22 changed files with 2063 additions and 168 deletions

View File

@@ -1,5 +1,4 @@
package com.ruoyi.web.controller.tool;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.annotation.Anonymous;
@@ -7,8 +6,11 @@ import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.system.service.IMarkService;
import cn.hutool.core.io.FileUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
@@ -17,21 +19,19 @@ import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.util.logging.Handler;
import java.util.*;
@RequestMapping("/tool/mark")
@RestController
@Anonymous
public class MarkController {
private static final String API_SECRET = "e10adc3949ba59abbe56e057f20f883e";
// erp_client_sb 服务地址
private static final String ERP_CLIENT_BASE_URL = "http://127.0.0.1:8081";
private final RestTemplate restTemplate = new RestTemplate();
private final ObjectMapper objectMapper = new ObjectMapper();
@Autowired
private RedisCache redisCache;
@Autowired
private IMarkService markService;
@@ -56,7 +56,83 @@ public class MarkController {
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
String result = restTemplate.postForObject("https://api.fangzhoujingxuan.com/Task", requestEntity, String.class);
JsonNode json = objectMapper.readTree(result);
return AjaxResult.success(json.get("D").asText());
if(json.get("S").asInt()==-1006){
token= markService.login();
formData.add("t", token);
requestEntity = new HttpEntity<>(formData, headers);
result = restTemplate.postForObject("https://api.fangzhoujingxuan.com/Task", requestEntity, String.class);
json= objectMapper.readTree(result);
}
JsonNode dNode = json.get("D").get("items").get(0);
// 获取下载链接并处理Excel数据
String downloadUrl = dNode.get("download_url").asText();
for (int i = 0; i < 6 && downloadUrl.isEmpty(); i++) {
Thread.sleep(5000);
long reTs = System.currentTimeMillis();
MultiValueMap<String, String> reFormData = new LinkedMultiValueMap<>();
reFormData.add("c", "TaskPageList");
reFormData.add("d", d);
reFormData.add("t", token);
reFormData.add("s", markService.md5(reTs + d + API_SECRET));
reFormData.add("ts", String.valueOf(reTs));
reFormData.add("website", "1");
HttpEntity<MultiValueMap<String, String>> reRequestEntity = new HttpEntity<>(reFormData, headers);
String reResult = restTemplate.postForObject("https://api.fangzhoujingxuan.com/Task", reRequestEntity, String.class);
JsonNode reJson = objectMapper.readTree(reResult);
dNode = reJson.get("D").get("items").get(0);
downloadUrl = reJson.get("D").get("items").get(0).get("download_url").asText();
}
String tempFilePath = System.getProperty("java.io.tmpdir") + "/trademark_" + System.currentTimeMillis() + ".xlsx";
HttpUtil.downloadFile(downloadUrl, FileUtil.file(tempFilePath));
List<Map<String, Object>> filteredData = new ArrayList<>();
ExcelReader reader = null;
try {
reader = ExcelUtil.getReader(FileUtil.file(tempFilePath));
List<List<Object>> rows = reader.read();
// 找到各列的索引
int asinIndex = -1, brandIndex = -1, trademarkTypeIndex = -1, registerDateIndex = -1, productImageIndex = -1;
if (!rows.isEmpty()) {
List<Object> header = rows.get(0);
for (int i = 0; i < header.size(); i++) {
String headerName = header.get(i).toString().trim();
if (headerName.equals("ASIN")) asinIndex = i;
else if (headerName.equals("品牌")) brandIndex = i;
else if (headerName.equals("商标类型")) trademarkTypeIndex = i;
else if (headerName.equals("注册时间")) registerDateIndex = i;
else if (headerName.equals("商品主图")) productImageIndex = i;
}
}
// 过滤TM和未注册数据
for (int i = 1; i < rows.size(); i++) {
List<Object> row = rows.get(i);
if (trademarkTypeIndex >= 0 && row.size() > trademarkTypeIndex) {
String trademarkType = row.get(trademarkTypeIndex).toString().trim();
if ("TM".equals(trademarkType) || "未注册".equals(trademarkType)) {
Map<String, Object> item = new HashMap<>();
if (asinIndex >= 0 && row.size() > asinIndex) item.put("ASIN", row.get(asinIndex));
if (brandIndex >= 0 && row.size() > brandIndex) item.put("品牌", row.get(brandIndex));
if (trademarkTypeIndex >= 0 && row.size() > trademarkTypeIndex) item.put("商标类型", row.get(trademarkTypeIndex));
if (registerDateIndex >= 0 && row.size() > registerDateIndex) item.put("注册时间", row.get(registerDateIndex));
if (productImageIndex >= 0 && row.size() > productImageIndex) item.put("商品主图", row.get(productImageIndex));
filteredData.add(item);
}
}
}
} finally {
if (reader != null) {
reader.close();
}
FileUtil.del(tempFilePath);
}
Map<String, Object> combinedResult = new HashMap<>();
combinedResult.put("original", dNode);
combinedResult.put("filtered", filteredData);
return AjaxResult.success(combinedResult);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -89,11 +165,49 @@ public class MarkController {
requestEntity = new HttpEntity<>(formData, headers);
result = restTemplate.postForObject("https://api.fangzhoujingxuan.com/Task", requestEntity, String.class);
jsonNode= objectMapper.readTree(result);
}
return jsonNode.get("S").asInt()==1?AjaxResult.success(result): AjaxResult.error( jsonNode.get("S").asText());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 品牌商标筛查(调用 erp_client_sb 服务)
* @param brands 品牌列表JSON数组
* @return 筛查结果
*/
@PostMapping("brandCheck")
public AjaxResult brandCheck(@RequestBody List<String> brands) {
try {
if (brands == null || brands.isEmpty()) {
return AjaxResult.error("品牌列表不能为空");
}
// 调用 erp_client_sb 的商标检查接口
String url = ERP_CLIENT_BASE_URL + "/api/trademark/brandCheck";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<List<String>> requestEntity = new HttpEntity<>(brands, headers);
// 调用远程服务
String result = restTemplate.postForObject(url, requestEntity, String.class);
JsonNode jsonNode = objectMapper.readTree(result);
// 判断返回状态 (erp_client_sb 的 JsonData: code=0 成功, code=-1 失败)
if (jsonNode.get("code").asInt() == 0) {
// 转换数据格式以适配前端
JsonNode data = jsonNode.get("data");
return AjaxResult.success(objectMapper.convertValue(data, Map.class));
} else {
String msg = jsonNode.has("msg") ? jsonNode.get("msg").asText() : "品牌筛查失败";
return AjaxResult.error(msg);
}
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("品牌筛查失败: " + e.getMessage());
}
}
}