提交昨晚更新改动

This commit is contained in:
super
2026-07-14 12:50:17 +08:00
parent d8ca8de537
commit 8a9126e626
21 changed files with 1104 additions and 173 deletions
@@ -111,19 +111,28 @@ public class PriceTrackController {
@GetMapping("/tasks/{taskId}/skip-asins/paginated")
@Operation(
summary = "分页查询任务的跳过 ASIN 数据",
description = "根据任务 ID 分页拉取该任务关联的 ASIN 数据。"
+ "全量模式:从 biz_skip_price_asin 表查询;"
+ "文件模式:从任务 requestJson 中存储的上传文件数据查询。"
+ "国家代码从任务的 requestJson.countryCodes 中获取"
description = "根据任务编号分页拉取该任务关联的 ASIN 数据。"
+ "全量模式:按任务店铺和国家从跳过跟价库查询;"
+ "文件模式:从任务保存的上传文件解析数据查询。"
+ "未传国家过滤条件时,默认使用任务创建时选择的国家"
)
public ApiResponse<SkipPriceAsinPageVo> getTaskSkipAsinsPaginated(
@Parameter(description = "任务 ID", required = true, example = "200")
@Parameter(description = "任务编号", required = true, example = "200")
@PathVariable Long taskId,
@Parameter(description = "页码,从 1 开始", example = "1")
@RequestParam(value = "page", defaultValue = "1") Integer page,
@Parameter(description = "每页条数,默认 1000,最大 2000", example = "1000")
@RequestParam(value = "page_size", defaultValue = "1000") Integer pageSize) {
return ApiResponse.success(priceTrackTaskService.getTaskSkipAsinsPaginated(taskId, page, pageSize));
@RequestParam(value = "page_size", defaultValue = "1000") Integer pageSize,
@Parameter(description = "店铺名称过滤条件,可重复传入,也可用英文逗号分隔;仅在任务关联店铺范围内生效", example = "测试店铺A")
@RequestParam(value = "shop_name", required = false) List<String> shopNames,
@Parameter(description = "国家代码过滤条件,可重复传入,也可用英文逗号分隔;仅在任务国家范围内生效", example = "DE")
@RequestParam(value = "country_code", required = false) List<String> countryCodes) {
return ApiResponse.success(priceTrackTaskService.getTaskSkipAsinsPaginated(
taskId,
page,
pageSize,
shopNames,
countryCodes));
}
@GetMapping("/tasks/{taskId}/skip-asin/check")
@@ -251,7 +260,7 @@ public class PriceTrackController {
+ "未完成/处理中:本次只传增量行数据,shop.success 不传或传 null,后端仅合并缓存继续等待。"
+ "已完成:传最后一批数据并设置 shop.success=true,后端会使用该店铺累计后的完整数据出结果。"
+ "失败:传 shop.success=false 或传非空 shop.error。"
+ "行级数据只保留表头字段:shopMallName、asin、price、recommendedPrice、shippingFee、minimumPrice、firstPlace、secondPlace、cartShopName、priceChangeStatus、modifyCount、status。"
+ "行级数据只保留表头字段:shopMallName、asin、price、recommendedPrice、shippingFee、minimumPrice、firstPlace、firstShop、secondPlace、secondShop、cartShopName、priceChangeStatus、modifyCount、status。"
)
public ApiResponse<Void> submitResult(
@Parameter(
@@ -83,9 +83,17 @@ public class PriceTrackSubmitResultRequest {
@Schema(description = "第一名", example = "竞对A")
private String firstPlace;
@JsonAlias({"first_shop", "firstPlaceShop", "first_place_shop", "第一名店铺名"})
@Schema(description = "第一名店铺名", example = "店铺A")
private String firstShop;
@Schema(description = "第二名", example = "竞对B")
private String secondPlace;
@JsonAlias({"second_shop", "secondPlaceShop", "second_place_shop", "第二名店铺名"})
@Schema(description = "第二名店铺名", example = "店铺B")
private String secondShop;
@Schema(description = "购物车店铺名", example = "店铺A")
private String cartShopName;
@@ -9,47 +9,47 @@ import java.util.List;
import java.util.Map;
/**
* Price-track skipped ASIN page response.
* 跟价任务跳过 ASIN 分页响应。
*/
@Data
@Schema(description = "Price-track skipped ASIN page response")
@Schema(description = "跟价任务跳过 ASIN 分页响应")
public class SkipPriceAsinPageVo {
@Schema(description = "Current page, starting from 1")
@Schema(description = "当前页码,从 1 开始")
private Integer page;
@Schema(description = "Page size")
@Schema(description = "每页条数")
private Integer pageSize;
@Schema(description = "Total records")
@Schema(description = "总记录数")
private Long total;
@Schema(description = "Total pages")
@Schema(description = "总页数")
private Integer totalPages;
@Schema(description = "ASIN list grouped by country")
@Schema(description = "按国家分组的 ASIN 列表")
private Map<String, List<String>> skipAsinsByCountry = new LinkedHashMap<>();
@Schema(description = "ASIN detail list grouped by country, including asin and minimumPrice")
@Schema(description = "按国家分组的 ASIN 明细列表,包含 ASIN 和最低价")
private Map<String, List<Map<String, String>>> skipAsinDetailsByCountry = new LinkedHashMap<>();
@JsonProperty("skip_asins")
@Schema(description = "Legacy Python queue field: ASIN list grouped by country")
@Schema(description = "兼容旧版队列字段:按国家分组的 ASIN 列表")
private Map<String, List<String>> skipAsins = new LinkedHashMap<>();
@JsonProperty("skip_asins_by_country")
@Schema(description = "Legacy Python queue field: ASIN list grouped by country")
@Schema(description = "兼容旧版队列字段:按国家分组的 ASIN 列表")
private Map<String, List<String>> skipAsinsByCountryLegacy = new LinkedHashMap<>();
@JsonProperty("skip_asin_details_by_country")
@Schema(description = "Legacy Python queue field: ASIN detail list grouped by country")
@Schema(description = "兼容旧版队列字段:按国家分组的 ASIN 明细列表")
private Map<String, List<Map<String, String>>> skipAsinDetailsByCountryLegacy = new LinkedHashMap<>();
@JsonProperty("asin_rows_by_country")
@Schema(description = "Legacy Python queue field: appointed ASIN rows grouped by country")
@Schema(description = "兼容旧版队列字段:按国家分组的指定 ASIN 文件行数据")
private Map<String, List<Map<String, String>>> asinRowsByCountry = new LinkedHashMap<>();
@JsonProperty("minimum_price_by_country_and_asin")
@Schema(description = "Legacy Python queue field: minimum price map grouped by country and ASIN")
@Schema(description = "兼容旧版队列字段:按国家和 ASIN 分组的最低价映射")
private Map<String, Map<String, String>> minimumPriceByCountryAndAsin = new LinkedHashMap<>();
}
@@ -27,7 +27,9 @@ public class PriceTrackExcelAssemblyService {
"运费",
"最低价",
"第一名",
"第一名店铺名",
"第二名",
"第二名店铺名",
"购物车店铺名",
"改价价格",
"改价情况",
@@ -62,12 +64,14 @@ public class PriceTrackExcelAssemblyService {
row.createCell(4).setCellValue(valueOf(item.getShippingFee()));
row.createCell(5).setCellValue(valueOf(item.getMinimumPrice()));
row.createCell(6).setCellValue(valueOf(item.getFirstPlace()));
row.createCell(7).setCellValue(valueOf(item.getSecondPlace()));
row.createCell(8).setCellValue(valueOf(item.getCartShopName()));
row.createCell(9).setCellValue(valueOf(item.getPriceChangePrice()));
row.createCell(10).setCellValue(valueOf(item.getPriceChangeStatus()));
row.createCell(11).setCellValue(valueOf(item.getModifyCount()));
row.createCell(12).setCellValue(valueOf(item.getStatus()));
row.createCell(7).setCellValue(valueOf(item.getFirstShop()));
row.createCell(8).setCellValue(valueOf(item.getSecondPlace()));
row.createCell(9).setCellValue(valueOf(item.getSecondShop()));
row.createCell(10).setCellValue(valueOf(item.getCartShopName()));
row.createCell(11).setCellValue(valueOf(item.getPriceChangePrice()));
row.createCell(12).setCellValue(valueOf(item.getPriceChangeStatus()));
row.createCell(13).setCellValue(valueOf(item.getModifyCount()));
row.createCell(14).setCellValue(valueOf(item.getStatus()));
}
applyDefaultColumnWidths(sheet);
}
@@ -127,7 +131,7 @@ public class PriceTrackExcelAssemblyService {
}
private void applyDefaultColumnWidths(Sheet sheet) {
int[] widths = {22, 18, 12, 12, 12, 12, 20, 20, 22, 14, 18, 12, 12};
int[] widths = {22, 18, 12, 12, 12, 12, 20, 22, 20, 22, 22, 14, 18, 12, 12};
for (int i = 0; i < widths.length; i++) {
sheet.setColumnWidth(i, widths[i] * 256);
}
@@ -48,6 +48,7 @@ import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -816,6 +817,11 @@ public class PriceTrackTaskService {
*/
public com.nanri.aiimage.modules.pricetrack.model.vo.SkipPriceAsinPageVo getTaskSkipAsinsPaginated(
Long taskId, int page, int pageSize) {
return getTaskSkipAsinsPaginated(taskId, page, pageSize, List.of(), List.of());
}
public com.nanri.aiimage.modules.pricetrack.model.vo.SkipPriceAsinPageVo getTaskSkipAsinsPaginated(
Long taskId, int page, int pageSize, List<String> requestedShopNames, List<String> requestedCountryCodes) {
if (taskId == null || taskId <= 0) {
throw new BusinessException("taskId 不合法");
}
@@ -827,8 +833,10 @@ public class PriceTrackTaskService {
PriceTrackCreateTaskRequest request = parseTaskRequest(task);
// Resolve country codes from task request.
List<String> countryCodes = request.getCountryCodes();
List<String> countryCodes = resolveSkipAsinCountryCodes(request, requestedCountryCodes);
if (countryCodes.isEmpty()) {
return emptySkipAsinPage(List.of(), page, pageSize);
}
// Choose full mode or uploaded-ASIN file mode.
boolean isAsinMode = request.isAsinMode();
@@ -841,11 +849,116 @@ public class PriceTrackTaskService {
// File mode reads parsed uploaded ASIN rows.
return getTaskAsinFilePaginated(request, countryCodes, page, pageSize);
} else {
// Full mode uses all ASINs and minimum prices from biz_skip_price_asin.
return skipPriceAsinService.listSkipAsinsPaginated(List.of(), countryCodes, page, pageSize);
List<String> shopNames = resolveSkipAsinShopNames(request, requestedShopNames);
if (shopNames.isEmpty()) {
return emptySkipAsinPage(countryCodes, page, pageSize);
}
return skipPriceAsinService.listSkipAsinsPaginated(shopNames, countryCodes, page, pageSize);
}
}
private List<String> resolveSkipAsinCountryCodes(PriceTrackCreateTaskRequest request, List<String> requestedCountryCodes) {
List<String> taskCountryCodes = normalizeTaskCountryCodes(request == null ? null : request.getCountryCodes());
LinkedHashSet<String> requested = new LinkedHashSet<>();
for (String country : splitQueryValues(requestedCountryCodes)) {
requested.add(normalizeLookupCountry(country));
}
if (requested.isEmpty()) {
return taskCountryCodes;
}
List<String> result = new ArrayList<>();
for (String country : requested) {
if (isTaskCountryEnabled(taskCountryCodes, country)) {
result.add(country);
}
}
return result;
}
private List<String> normalizeTaskCountryCodes(List<String> countryCodes) {
LinkedHashSet<String> normalized = new LinkedHashSet<>();
if (countryCodes != null) {
for (String country : countryCodes) {
String value = country == null ? "" : country.trim().toUpperCase(Locale.ROOT);
if (!value.isEmpty()) {
normalized.add(value);
}
}
}
return new ArrayList<>(normalized);
}
private List<String> resolveSkipAsinShopNames(PriceTrackCreateTaskRequest request, List<String> requestedShopNames) {
List<String> taskShopNames = resolveTaskShopNames(request);
List<String> requested = splitQueryValues(requestedShopNames);
LinkedHashSet<String> normalized = new LinkedHashSet<>();
for (String shopName : requested) {
String value = ziniaoShopSwitchService.normalizeShopName(shopName);
if (!value.isBlank() && (taskShopNames.isEmpty() || taskShopNames.contains(value))) {
normalized.add(value);
}
}
if (!normalized.isEmpty()) {
return new ArrayList<>(normalized);
}
if (!requested.isEmpty()) {
return List.of();
}
return taskShopNames;
}
private List<String> splitQueryValues(List<String> values) {
List<String> result = new ArrayList<>();
if (values == null) {
return result;
}
for (String value : values) {
if (value == null) {
continue;
}
for (String part : value.split(",")) {
String trimmed = part.trim();
if (!trimmed.isEmpty()) {
result.add(trimmed);
}
}
}
return result;
}
private com.nanri.aiimage.modules.pricetrack.model.vo.SkipPriceAsinPageVo emptySkipAsinPage(
List<String> countryCodes, int page, int pageSize) {
if (page < 1) {
page = 1;
}
if (pageSize < 1 || pageSize > 2000) {
pageSize = 1000;
}
List<String> targetCountries = (countryCodes == null || countryCodes.isEmpty())
? List.of("DE", "UK", "FR", "IT", "ES")
: countryCodes;
Map<String, List<String>> skipAsinsByCountry = new LinkedHashMap<>();
Map<String, List<Map<String, String>>> skipDetailsByCountry = new LinkedHashMap<>();
for (String country : targetCountries) {
skipAsinsByCountry.put(country, new ArrayList<>());
skipDetailsByCountry.put(country, new ArrayList<>());
}
com.nanri.aiimage.modules.pricetrack.model.vo.SkipPriceAsinPageVo vo =
new com.nanri.aiimage.modules.pricetrack.model.vo.SkipPriceAsinPageVo();
vo.setPage(page);
vo.setPageSize(pageSize);
vo.setTotal(0L);
vo.setTotalPages(0);
vo.setSkipAsinsByCountry(skipAsinsByCountry);
vo.setSkipAsinDetailsByCountry(skipDetailsByCountry);
vo.setSkipAsins(skipAsinsByCountry);
vo.setSkipAsinsByCountryLegacy(skipAsinsByCountry);
vo.setSkipAsinDetailsByCountryLegacy(skipDetailsByCountry);
vo.setAsinRowsByCountry(new LinkedHashMap<>());
vo.setMinimumPriceByCountryAndAsin(new LinkedHashMap<>());
return vo;
}
private List<String> resolveTaskShopNames(PriceTrackCreateTaskRequest request) {
List<String> shopNames = new ArrayList<>();
if (request == null || request.getItems() == null) {
@@ -1391,7 +1504,9 @@ public class PriceTrackTaskService {
case "推荐价", "recommendedprice" -> "recommendedPrice";
case "最低价", "minimumprice" -> "minimumPrice";
case "第一名", "firstplace" -> "firstPlace";
case "第一名店铺名", "firstshop", "firstplaceshop" -> "firstShop";
case "第二名", "secondplace" -> "secondPlace";
case "第二名店铺名", "secondshop", "secondplaceshop" -> "secondShop";
case "购物车店铺名", "cartshopname" -> "cartShopName";
case "改价情况", "pricechangestatus" -> "priceChangeStatus";
case "修改次数", "modifycount" -> "modifyCount";
@@ -1661,7 +1776,9 @@ public class PriceTrackTaskService {
merged.setShippingFee(firstNonBlank(incoming.getShippingFee(), merged.getShippingFee()));
merged.setMinimumPrice(firstNonBlank(incoming.getMinimumPrice(), merged.getMinimumPrice()));
merged.setFirstPlace(firstNonBlank(incoming.getFirstPlace(), merged.getFirstPlace()));
merged.setFirstShop(firstNonBlank(incoming.getFirstShop(), merged.getFirstShop()));
merged.setSecondPlace(firstNonBlank(incoming.getSecondPlace(), merged.getSecondPlace()));
merged.setSecondShop(firstNonBlank(incoming.getSecondShop(), merged.getSecondShop()));
merged.setCartShopName(firstNonBlank(incoming.getCartShopName(), merged.getCartShopName()));
merged.setPriceChangePrice(firstNonBlank(incoming.getPriceChangePrice(), merged.getPriceChangePrice()));
merged.setPriceChangeStatus(firstNonBlank(incoming.getPriceChangeStatus(), merged.getPriceChangeStatus()));
@@ -1686,7 +1803,9 @@ public class PriceTrackTaskService {
out.setShippingFee(row.getShippingFee());
out.setMinimumPrice(row.getMinimumPrice());
out.setFirstPlace(row.getFirstPlace());
out.setFirstShop(row.getFirstShop());
out.setSecondPlace(row.getSecondPlace());
out.setSecondShop(row.getSecondShop());
out.setCartShopName(row.getCartShopName());
out.setPriceChangePrice(row.getPriceChangePrice());
out.setPriceChangeStatus(row.getPriceChangeStatus());