feat(amazon): 更新商标筛查界面图标与状态展示- 将商标筛查状态图标从图片替换为 SVG 图标
- 添加了进行中、取消、完成/失败状态的 SVG 图标 -优化任务进度指示器,使用 SVG 并支持旋转动画 - 禁用跟卖许可筛查功能并更新提示文案 - 调整标签页样式和间距,适配不同屏幕尺寸 -修复状态横幅图标显示问题,并统一图标尺寸 - 更新全局样式以提升视觉一致性和用户体验
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
</parent>
|
||||
<groupId>com.tashow.erp</groupId>
|
||||
<artifactId>erp_client_sb</artifactId>
|
||||
<version>2.6.2</version>
|
||||
<version>2.6.3</version>
|
||||
<name>erp_client_sb</name>
|
||||
<description>erp客户端</description>
|
||||
<properties>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
package com.tashow.erp.controller;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.tashow.erp.entity.TrademarkSessionEntity;
|
||||
@@ -17,9 +18,11 @@ import org.slf4j.Logger;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 商标检查控制器
|
||||
*/
|
||||
@@ -29,22 +32,22 @@ import java.util.stream.Collectors;
|
||||
public class TrademarkController {
|
||||
private static final Logger logger = LoggerUtil.getLogger(TrademarkController.class);
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
|
||||
@Autowired
|
||||
private TrademarkCheckUtil util;
|
||||
|
||||
|
||||
@Autowired
|
||||
private BrandTrademarkCacheService cacheService;
|
||||
|
||||
|
||||
@Autowired
|
||||
private TrademarkSessionRepository sessionRepository;
|
||||
|
||||
|
||||
@Autowired
|
||||
private IFangzhouApiService fangzhouApi;
|
||||
|
||||
|
||||
// 进度追踪
|
||||
private final Map<String, Integer> progressMap = new java.util.concurrent.ConcurrentHashMap<>();
|
||||
|
||||
|
||||
// 任务取消标志
|
||||
private final Map<String, Boolean> cancelMap = new java.util.concurrent.ConcurrentHashMap<>();
|
||||
|
||||
@@ -56,9 +59,7 @@ public class TrademarkController {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> brands = (List<String>) request.get("brands");
|
||||
String taskId = (String) request.get("taskId");
|
||||
|
||||
try {
|
||||
// 保持与前端传入数量一致,不去重(允许重复品牌以匹配产品数量)
|
||||
List<String> list = brands.stream()
|
||||
.filter(b -> b != null && !b.trim().isEmpty())
|
||||
.map(String::trim)
|
||||
@@ -70,9 +71,6 @@ public class TrademarkController {
|
||||
List<String> toQuery = list.stream()
|
||||
.filter(b -> !cached.containsKey(b))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
logger.info("全局缓存命中: {}/{},需查询: {}", cached.size(), list.size(), toQuery.size());
|
||||
// 3. 查询未命中的品牌
|
||||
Map<String, Boolean> queried = new HashMap<>();
|
||||
if (!toQuery.isEmpty()) {
|
||||
for (int i = 0; i < toQuery.size(); i++) {
|
||||
@@ -81,32 +79,32 @@ public class TrademarkController {
|
||||
logger.info("任务 {} 已被取消,停止查询", taskId);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
String brand = toQuery.get(i);
|
||||
logger.info("处理第 {} 个: {}", i + 1, brand);
|
||||
|
||||
|
||||
Map<String, Boolean> results = util.batchCheck(Collections.singletonList(brand), queried);
|
||||
queried.putAll(results);
|
||||
|
||||
|
||||
// 更新进度
|
||||
if (taskId != null) {
|
||||
progressMap.put(taskId, cached.size() + queried.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 查询结束,保存所有品牌
|
||||
if (!queried.isEmpty())
|
||||
cacheService.saveResults(queried);
|
||||
if (!queried.isEmpty())
|
||||
cacheService.saveResults(queried);
|
||||
}
|
||||
|
||||
|
||||
// 5. 合并缓存和新查询结果
|
||||
Map<String, Boolean> allResults = new HashMap<>(cached);
|
||||
allResults.putAll(queried);
|
||||
|
||||
|
||||
// 6. 统计结果
|
||||
List<Map<String, Object>> unregistered = new ArrayList<>();
|
||||
int registeredCount = 0;
|
||||
|
||||
|
||||
for (Map.Entry<String, Boolean> entry : allResults.entrySet()) {
|
||||
if (!entry.getValue()) {
|
||||
Map<String, Object> m = new HashMap<>();
|
||||
@@ -117,11 +115,11 @@ public class TrademarkController {
|
||||
registeredCount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
long t = (System.currentTimeMillis() - start) / 1000;
|
||||
int checkedCount = list.size();
|
||||
int failedCount = 0;
|
||||
|
||||
int failedCount = 0;
|
||||
|
||||
Map<String, Object> res = new HashMap<>();
|
||||
res.put("total", list.size());
|
||||
res.put("checked", checkedCount);
|
||||
@@ -131,19 +129,22 @@ public class TrademarkController {
|
||||
res.put("data", unregistered);
|
||||
res.put("duration", t + "秒");
|
||||
|
||||
logger.info("完成: 共{}个,成功查询{}个(已注册{}个,未注册{}个),查询失败{}个,耗时{}秒",
|
||||
list.size(), checkedCount, registeredCount, unregistered.size(), failedCount, t);
|
||||
|
||||
logger.info("完成: 共{}个,成功查询{}个(已注册{}个,未注册{}个),查询失败{}个,耗时{}秒",
|
||||
list.size(), checkedCount, registeredCount, unregistered.size(), failedCount, t);
|
||||
|
||||
// 30秒后清理进度和取消标志
|
||||
if (taskId != null) {
|
||||
String finalTaskId = taskId;
|
||||
new Thread(() -> {
|
||||
try { Thread.sleep(30000); } catch (InterruptedException ignored) {}
|
||||
try {
|
||||
Thread.sleep(30000);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
progressMap.remove(finalTaskId);
|
||||
cancelMap.remove(finalTaskId);
|
||||
}).start();
|
||||
}
|
||||
|
||||
|
||||
return JsonData.buildSuccess(res);
|
||||
} catch (Exception e) {
|
||||
logger.error("筛查失败", e);
|
||||
@@ -153,7 +154,7 @@ public class TrademarkController {
|
||||
cacheService.cleanExpired();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询品牌筛查进度
|
||||
*/
|
||||
@@ -167,7 +168,7 @@ public class TrademarkController {
|
||||
result.put("current", current);
|
||||
return JsonData.buildSuccess(result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 取消品牌筛查任务
|
||||
*/
|
||||
@@ -192,41 +193,41 @@ public class TrademarkController {
|
||||
Map<String, Object> fullData = ExcelParseUtil.parseFullExcel(file);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> headers = (List<String>) fullData.get("headers");
|
||||
|
||||
|
||||
if (headers == null || headers.isEmpty()) {
|
||||
return JsonData.buildError("无法读取Excel表头");
|
||||
}
|
||||
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("headers", headers);
|
||||
|
||||
|
||||
// 如果提供了必需表头,进行验证
|
||||
if (requiredHeadersJson != null && !requiredHeadersJson.trim().isEmpty()) {
|
||||
List<String> requiredHeaders = objectMapper.readValue(requiredHeadersJson,
|
||||
objectMapper.getTypeFactory().constructCollectionType(List.class, String.class));
|
||||
|
||||
objectMapper.getTypeFactory().constructCollectionType(List.class, String.class));
|
||||
|
||||
List<String> missing = new ArrayList<>();
|
||||
for (String required : requiredHeaders) {
|
||||
if (!headers.contains(required)) {
|
||||
missing.add(required);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
result.put("valid", missing.isEmpty());
|
||||
result.put("missing", missing);
|
||||
|
||||
|
||||
if (!missing.isEmpty()) {
|
||||
return JsonData.buildError("缺少必需的列: " + String.join(", ", missing));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return JsonData.buildSuccess(result);
|
||||
} catch (Exception e) {
|
||||
logger.error("验证表头失败", e);
|
||||
return JsonData.buildError("验证失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从Excel提取品牌列表(同时返回完整Excel数据)
|
||||
*/
|
||||
@@ -235,10 +236,10 @@ public class TrademarkController {
|
||||
try {
|
||||
List<String> brands = ExcelParseUtil.parseColumnByName(file, "品牌");
|
||||
if (brands.isEmpty()) return JsonData.buildError("未找到品牌列或品牌数据为空");
|
||||
|
||||
|
||||
// 读取完整Excel数据
|
||||
Map<String, Object> fullData = ExcelParseUtil.parseFullExcel(file);
|
||||
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("total", brands.size());
|
||||
result.put("brands", brands);
|
||||
@@ -259,35 +260,35 @@ public class TrademarkController {
|
||||
if (asinsJson == null || asinsJson.trim().isEmpty()) {
|
||||
return JsonData.buildError("ASIN列表不能为空");
|
||||
}
|
||||
|
||||
|
||||
// 使用Jackson解析JSON数组
|
||||
List<String> asins;
|
||||
try {
|
||||
asins = objectMapper.readValue(asinsJson,
|
||||
objectMapper.getTypeFactory().constructCollectionType(List.class, String.class));
|
||||
asins = objectMapper.readValue(asinsJson,
|
||||
objectMapper.getTypeFactory().constructCollectionType(List.class, String.class));
|
||||
} catch (Exception e) {
|
||||
logger.error("解析ASIN列表JSON失败: {}", asinsJson, e);
|
||||
return JsonData.buildError("ASIN列表格式错误: " + e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
if (asins == null || asins.isEmpty()) {
|
||||
return JsonData.buildError("ASIN列表不能为空");
|
||||
}
|
||||
|
||||
|
||||
logger.info("接收到ASIN过滤请求,ASIN数量: {}", asins.size());
|
||||
|
||||
|
||||
Map<String, Object> result = ExcelParseUtil.filterExcelByAsins(file, asins);
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> filteredRows = (List<Map<String, Object>>) result.get("filteredRows");
|
||||
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("headers", result.get("headers"));
|
||||
response.put("filteredRows", filteredRows);
|
||||
response.put("total", filteredRows.size());
|
||||
|
||||
|
||||
logger.info("ASIN过滤完成,过滤出 {} 行数据", filteredRows.size());
|
||||
|
||||
|
||||
return JsonData.buildSuccess(response);
|
||||
} catch (Exception e) {
|
||||
logger.error("根据ASIN过滤失败", e);
|
||||
@@ -304,35 +305,35 @@ public class TrademarkController {
|
||||
if (brandsJson == null || brandsJson.trim().isEmpty()) {
|
||||
return JsonData.buildError("品牌列表不能为空");
|
||||
}
|
||||
|
||||
|
||||
// 使用Jackson解析JSON数组
|
||||
List<String> brands;
|
||||
try {
|
||||
brands = objectMapper.readValue(brandsJson,
|
||||
objectMapper.getTypeFactory().constructCollectionType(List.class, String.class));
|
||||
brands = objectMapper.readValue(brandsJson,
|
||||
objectMapper.getTypeFactory().constructCollectionType(List.class, String.class));
|
||||
} catch (Exception e) {
|
||||
logger.error("解析品牌列表JSON失败: {}", brandsJson, e);
|
||||
return JsonData.buildError("品牌列表格式错误: " + e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
if (brands == null || brands.isEmpty()) {
|
||||
return JsonData.buildError("品牌列表不能为空");
|
||||
}
|
||||
|
||||
|
||||
logger.info("接收到品牌过滤请求,品牌数量: {}", brands.size());
|
||||
|
||||
|
||||
Map<String, Object> result = ExcelParseUtil.filterExcelByBrands(file, brands);
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> filteredRows = (List<Map<String, Object>>) result.get("filteredRows");
|
||||
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("headers", result.get("headers"));
|
||||
response.put("filteredRows", filteredRows);
|
||||
response.put("total", filteredRows.size());
|
||||
|
||||
|
||||
logger.info("品牌过滤完成,过滤出 {} 行数据", filteredRows.size());
|
||||
|
||||
|
||||
return JsonData.buildSuccess(response);
|
||||
} catch (Exception e) {
|
||||
logger.error("根据品牌过滤失败", e);
|
||||
@@ -344,13 +345,13 @@ public class TrademarkController {
|
||||
* 保存商标查询会话
|
||||
*/
|
||||
@PostMapping("/saveSession")
|
||||
public JsonData saveSession(@RequestBody Map<String, Object> sessionData,
|
||||
public JsonData saveSession(@RequestBody Map<String, Object> sessionData,
|
||||
@RequestHeader(value = "username", required = false) String username) {
|
||||
try {
|
||||
if (username == null || username.trim().isEmpty()) {
|
||||
username = "default";
|
||||
}
|
||||
|
||||
|
||||
String sessionId = UUID.randomUUID().toString();
|
||||
TrademarkSessionEntity entity = new TrademarkSessionEntity();
|
||||
entity.setSessionId(sessionId);
|
||||
@@ -361,14 +362,14 @@ public class TrademarkController {
|
||||
entity.setHeaders(objectMapper.writeValueAsString(sessionData.get("headers")));
|
||||
entity.setTaskProgress(objectMapper.writeValueAsString(sessionData.get("taskProgress")));
|
||||
entity.setQueryStatus((String) sessionData.get("queryStatus"));
|
||||
|
||||
|
||||
sessionRepository.save(entity);
|
||||
|
||||
|
||||
// 清理7天前的数据
|
||||
sessionRepository.deleteByCreatedAtBefore(LocalDateTime.now().minusDays(7));
|
||||
|
||||
|
||||
logger.info("保存商标查询会话: {} (用户: {})", sessionId, username);
|
||||
|
||||
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put("sessionId", sessionId);
|
||||
return JsonData.buildSuccess(result);
|
||||
@@ -383,17 +384,17 @@ public class TrademarkController {
|
||||
*/
|
||||
@GetMapping("/getSession")
|
||||
public JsonData getSession(@RequestParam("sessionId") String sessionId,
|
||||
@RequestHeader(value = "username", required = false) String username) {
|
||||
@RequestHeader(value = "username", required = false) String username) {
|
||||
try {
|
||||
if (username == null || username.trim().isEmpty()) {
|
||||
username = "default";
|
||||
}
|
||||
|
||||
|
||||
Optional<TrademarkSessionEntity> opt = sessionRepository.findBySessionIdAndUsername(sessionId, username);
|
||||
if (!opt.isPresent()) {
|
||||
return JsonData.buildError("会话不存在或已过期");
|
||||
}
|
||||
|
||||
|
||||
TrademarkSessionEntity entity = opt.get();
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("fileName", entity.getFileName());
|
||||
@@ -402,7 +403,7 @@ public class TrademarkController {
|
||||
result.put("headers", objectMapper.readValue(entity.getHeaders(), List.class));
|
||||
result.put("taskProgress", objectMapper.readValue(entity.getTaskProgress(), Map.class));
|
||||
result.put("queryStatus", entity.getQueryStatus());
|
||||
|
||||
|
||||
logger.info("恢复商标查询会话: {} (用户: {})", sessionId, username);
|
||||
return JsonData.buildSuccess(result);
|
||||
} catch (Exception e) {
|
||||
@@ -412,7 +413,7 @@ public class TrademarkController {
|
||||
}
|
||||
|
||||
// ==================== 方舟精选任务管理接口 ====================
|
||||
|
||||
|
||||
/**
|
||||
* 获取方舟精选任务列表
|
||||
* 从第三方 API 下载 Excel 并解析过滤数据
|
||||
@@ -424,33 +425,33 @@ public class TrademarkController {
|
||||
String token = fangzhouApi.getToken();
|
||||
JsonNode dNode = fangzhouApi.pollTask(token, 6, 5000);
|
||||
String downloadUrl = dNode.get("download_url").asText();
|
||||
|
||||
|
||||
if (downloadUrl == null || downloadUrl.isEmpty()) {
|
||||
return JsonData.buildError("下载链接生成超时");
|
||||
}
|
||||
|
||||
|
||||
// 2. 下载并解析 Excel
|
||||
String tempFilePath = System.getProperty("java.io.tmpdir") + "/trademark_" + System.currentTimeMillis() + ".xlsx";
|
||||
HttpUtil.downloadFile(downloadUrl, FileUtil.file(tempFilePath));
|
||||
|
||||
|
||||
List<Map<String, Object>> filteredData = new ArrayList<>();
|
||||
List<String> excelHeaders = new ArrayList<>();
|
||||
ExcelReader reader = null;
|
||||
|
||||
|
||||
try {
|
||||
reader = ExcelUtil.getReader(FileUtil.file(tempFilePath));
|
||||
List<List<Object>> rows = reader.read();
|
||||
|
||||
|
||||
if (rows.isEmpty()) {
|
||||
throw new RuntimeException("Excel文件为空");
|
||||
}
|
||||
|
||||
|
||||
// 读取表头
|
||||
List<Object> headerRow = rows.get(0);
|
||||
for (Object cell : headerRow) {
|
||||
excelHeaders.add(cell != null ? cell.toString().trim() : "");
|
||||
}
|
||||
|
||||
|
||||
// 找到商标类型列的索引
|
||||
int trademarkTypeIndex = -1;
|
||||
for (int i = 0; i < excelHeaders.size(); i++) {
|
||||
@@ -459,11 +460,11 @@ public class TrademarkController {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (trademarkTypeIndex < 0) {
|
||||
throw new RuntimeException("未找到'商标类型'列");
|
||||
}
|
||||
|
||||
|
||||
// 过滤TM和未注册数据
|
||||
for (int i = 1; i < rows.size(); i++) {
|
||||
List<Object> row = rows.get(i);
|
||||
@@ -484,16 +485,16 @@ public class TrademarkController {
|
||||
}
|
||||
FileUtil.del(tempFilePath);
|
||||
}
|
||||
|
||||
|
||||
// 6. 返回结果
|
||||
Map<String, Object> combinedResult = new HashMap<>();
|
||||
combinedResult.put("original", dNode);
|
||||
combinedResult.put("filtered", filteredData);
|
||||
combinedResult.put("headers", excelHeaders);
|
||||
|
||||
|
||||
logger.info("任务获取成功,过滤出 {} 条数据", filteredData.size());
|
||||
return JsonData.buildSuccess(combinedResult);
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("获取任务失败", e);
|
||||
return JsonData.buildError("获取任务失败: " + e.getMessage());
|
||||
@@ -510,13 +511,13 @@ public class TrademarkController {
|
||||
// 1. 获取 Token 并上传文件
|
||||
String token = fangzhouApi.getToken();
|
||||
JsonNode jsonNode = fangzhouApi.uploadFile(file, token);
|
||||
|
||||
|
||||
// 2. 返回结果
|
||||
if (jsonNode.get("S").asInt() == 1) {
|
||||
logger.info("任务创建成功: {}", file.getOriginalFilename());
|
||||
return JsonData.buildSuccess(jsonNode.toString());
|
||||
}
|
||||
|
||||
|
||||
return JsonData.buildError(jsonNode.get("M").asText());
|
||||
} catch (Exception e) {
|
||||
logger.error("创建任务失败", e);
|
||||
|
||||
@@ -47,8 +47,8 @@ server:
|
||||
api:
|
||||
server:
|
||||
# 主服务器API配置
|
||||
#base-url: "http://8.138.23.49:8085"
|
||||
base-url: "http://192.168.1.89:8085"
|
||||
base-url: "http://8.138.23.49:8085"
|
||||
# base-url: "http://192.168.1.89:8085"
|
||||
paths:
|
||||
monitor: "/monitor/client/api"
|
||||
login: "/monitor/account/login"
|
||||
|
||||
Reference in New Issue
Block a user