后端优化修复问题
This commit is contained in:
+16
@@ -20,6 +20,7 @@ import com.nanri.aiimage.modules.pricetrack.model.vo.PriceTrackLoopRunVo;
|
||||
import com.nanri.aiimage.modules.pricetrack.model.vo.PriceTrackMatchShopsVo;
|
||||
import com.nanri.aiimage.modules.pricetrack.model.vo.PriceTrackPendingDeleteVo;
|
||||
import com.nanri.aiimage.modules.pricetrack.model.vo.PriceTrackTaskBatchVo;
|
||||
import com.nanri.aiimage.modules.pricetrack.model.vo.SkipPriceAsinCheckVo;
|
||||
import com.nanri.aiimage.modules.pricetrack.model.vo.SkipPriceAsinPageVo;
|
||||
import com.nanri.aiimage.modules.pricetrack.service.PriceTrackLoopRunService;
|
||||
import com.nanri.aiimage.modules.pricetrack.service.PriceTrackService;
|
||||
@@ -125,6 +126,21 @@ public class PriceTrackController {
|
||||
return ApiResponse.success(priceTrackTaskService.getTaskSkipAsinsPaginated(taskId, page, pageSize));
|
||||
}
|
||||
|
||||
@GetMapping("/tasks/{taskId}/skip-asin/check")
|
||||
@Operation(
|
||||
summary = "检查单个跳过跟价 ASIN",
|
||||
description = "Python 按国家和单个 ASIN 查询是否命中跳过跟价库,避免分页加载全量跳过 ASIN。"
|
||||
)
|
||||
public ApiResponse<SkipPriceAsinCheckVo> checkTaskSkipAsin(
|
||||
@Parameter(description = "任务 ID", required = true, example = "200")
|
||||
@PathVariable Long taskId,
|
||||
@Parameter(description = "国家代码", required = true, example = "DE")
|
||||
@RequestParam("country") String country,
|
||||
@Parameter(description = "ASIN", required = true, example = "B0XXXX")
|
||||
@RequestParam("asin") String asin) {
|
||||
return ApiResponse.success(priceTrackTaskService.checkTaskSkipAsin(taskId, country, asin));
|
||||
}
|
||||
|
||||
@GetMapping("/dashboard")
|
||||
@Operation(summary = "统计看板", description = "返回备选店铺数、已结束任务数、成功任务数和失败任务数。")
|
||||
public ApiResponse<PriceTrackDashboardVo> dashboard(
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.nanri.aiimage.modules.pricetrack.model.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "single skip price asin lookup response")
|
||||
public class SkipPriceAsinCheckVo {
|
||||
|
||||
@Schema(description = "country code", example = "DE")
|
||||
private String country;
|
||||
|
||||
@Schema(description = "normalized asin", example = "B0XXXX")
|
||||
private String asin;
|
||||
|
||||
@Schema(description = "whether the asin exists in skip price asin table")
|
||||
private boolean exists;
|
||||
|
||||
@Schema(description = "configured minimum price, empty when not configured or not found")
|
||||
private String minimumPrice;
|
||||
}
|
||||
+79
-22
@@ -21,6 +21,8 @@ import com.nanri.aiimage.modules.pricetrack.model.vo.PriceTrackResultItemVo;
|
||||
import com.nanri.aiimage.modules.pricetrack.model.vo.PriceTrackTaskBatchVo;
|
||||
import com.nanri.aiimage.modules.pricetrack.model.vo.PriceTrackTaskDetailVo;
|
||||
import com.nanri.aiimage.modules.pricetrack.model.vo.PriceTrackTaskItemVo;
|
||||
import com.nanri.aiimage.modules.pricetrack.model.vo.SkipPriceAsinCheckVo;
|
||||
import com.nanri.aiimage.modules.shopkey.model.dto.SkipPriceAsinDetailDto;
|
||||
import com.nanri.aiimage.modules.shopkey.service.SkipPriceAsinService;
|
||||
import com.nanri.aiimage.modules.task.mapper.FileResultMapper;
|
||||
import com.nanri.aiimage.modules.task.mapper.FileTaskMapper;
|
||||
@@ -367,18 +369,10 @@ public class PriceTrackTaskService {
|
||||
}
|
||||
}
|
||||
|
||||
// 汇总店铺名并查询需要跳过的 ASIN
|
||||
Map<String, List<String>> skipAsinsByCountry = request.isAsinMode()
|
||||
? new LinkedHashMap<>()
|
||||
: skipPriceAsinService.listAllSkipAsinsByCountry();
|
||||
Map<String, List<Map<String, String>>> skipAsinDetailsByCountry = request.isAsinMode()
|
||||
? new LinkedHashMap<>()
|
||||
: skipPriceAsinService.listAllSkipAsinDetailsByCountry();
|
||||
Map<String, List<Map<String, String>>> asinRowsByCountry = request.isAsinMode()
|
||||
? parseAsinRowsByCountry(request.getAsinFiles(), request.getCountryCodes())
|
||||
: new LinkedHashMap<>();
|
||||
Map<String, Map<String, String>> minimumPriceByCountryAndAsin =
|
||||
buildMinimumPriceByCountryAndAsin(asinRowsByCountry);
|
||||
Map<String, List<String>> skipAsinsByCountry = new LinkedHashMap<>();
|
||||
Map<String, List<Map<String, String>>> skipAsinDetailsByCountry = new LinkedHashMap<>();
|
||||
Map<String, List<Map<String, String>>> asinRowsByCountry = new LinkedHashMap<>();
|
||||
Map<String, Map<String, String>> minimumPriceByCountryAndAsin = new LinkedHashMap<>();
|
||||
log.info("[price-track] createTask skipAsins countries={} asinMode={}",
|
||||
skipAsinsByCountry.keySet(), request.isAsinMode());
|
||||
|
||||
@@ -788,16 +782,7 @@ public class PriceTrackTaskService {
|
||||
throw new BusinessException("任务不存在");
|
||||
}
|
||||
|
||||
// 解析任务请求参数
|
||||
PriceTrackCreateTaskRequest request;
|
||||
try {
|
||||
if (task.getRequestJson() == null || task.getRequestJson().isBlank()) {
|
||||
throw new BusinessException("任务请求参数为空");
|
||||
}
|
||||
request = objectMapper.readValue(task.getRequestJson(), PriceTrackCreateTaskRequest.class);
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException("任务请求参数解析失败: " + e.getMessage());
|
||||
}
|
||||
PriceTrackCreateTaskRequest request = parseTaskRequest(task);
|
||||
|
||||
// 从任务中获取国家代码
|
||||
List<String> countryCodes = request.getCountryCodes();
|
||||
@@ -814,6 +799,78 @@ public class PriceTrackTaskService {
|
||||
}
|
||||
}
|
||||
|
||||
public SkipPriceAsinCheckVo checkTaskSkipAsin(Long taskId, String country, String asin) {
|
||||
if (taskId == null || taskId <= 0) {
|
||||
throw new BusinessException("taskId 不合法");
|
||||
}
|
||||
String normalizedCountry = normalizeLookupCountry(country);
|
||||
String normalizedAsin = normalizeLookupAsin(asin);
|
||||
|
||||
FileTaskEntity task = loadTaskForExecution(taskId);
|
||||
if (task == null || !MODULE_TYPE.equals(task.getModuleType())) {
|
||||
throw new BusinessException("任务不存在");
|
||||
}
|
||||
PriceTrackCreateTaskRequest request = parseTaskRequest(task);
|
||||
|
||||
SkipPriceAsinCheckVo vo = new SkipPriceAsinCheckVo();
|
||||
vo.setCountry(normalizedCountry);
|
||||
vo.setAsin(normalizedAsin);
|
||||
vo.setExists(false);
|
||||
vo.setMinimumPrice("");
|
||||
|
||||
if (request.isAsinMode() || !isTaskCountryEnabled(request.getCountryCodes(), normalizedCountry)) {
|
||||
return vo;
|
||||
}
|
||||
SkipPriceAsinDetailDto detail = skipPriceAsinService.findSkipAsinByCountryAndAsin(normalizedCountry, normalizedAsin);
|
||||
if (detail != null) {
|
||||
vo.setExists(true);
|
||||
vo.setMinimumPrice(detail.getMinimumPrice() == null ? "" : detail.getMinimumPrice());
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
private PriceTrackCreateTaskRequest parseTaskRequest(FileTaskEntity task) {
|
||||
try {
|
||||
if (task.getRequestJson() == null || task.getRequestJson().isBlank()) {
|
||||
throw new BusinessException("任务请求参数为空");
|
||||
}
|
||||
return objectMapper.readValue(task.getRequestJson(), PriceTrackCreateTaskRequest.class);
|
||||
} catch (BusinessException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
throw new BusinessException("任务请求参数解析失败: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isTaskCountryEnabled(List<String> countryCodes, String country) {
|
||||
if (countryCodes == null || countryCodes.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return countryCodes.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(code -> code.trim().toUpperCase(Locale.ROOT))
|
||||
.anyMatch(country::equals);
|
||||
}
|
||||
|
||||
private String normalizeLookupCountry(String country) {
|
||||
String normalized = country == null ? "" : country.trim().toUpperCase(Locale.ROOT);
|
||||
if (!List.of("DE", "UK", "FR", "IT", "ES").contains(normalized)) {
|
||||
throw new BusinessException("国家参数不支持: " + country);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private String normalizeLookupAsin(String asin) {
|
||||
String normalized = asin == null ? "" : asin.trim().toUpperCase(Locale.ROOT);
|
||||
if (normalized.isEmpty()) {
|
||||
throw new BusinessException("ASIN 不能为空");
|
||||
}
|
||||
if (normalized.length() > 64) {
|
||||
throw new BusinessException("ASIN 长度不能超过 64");
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件模式:从任务上传的文件中分页返回 ASIN 数据
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user