后台查询Asin文档更新
This commit is contained in:
+69
-133
@@ -1,6 +1,11 @@
|
||||
package com.nanri.aiimage.modules.queryasin.controller;
|
||||
|
||||
import com.nanri.aiimage.common.api.ApiResponse;
|
||||
import com.nanri.aiimage.modules.productrisk.model.dto.ProductRiskCandidateAddRequest;
|
||||
import com.nanri.aiimage.modules.productrisk.model.dto.ProductRiskMatchShopsRequest;
|
||||
import com.nanri.aiimage.modules.productrisk.model.vo.ProductRiskCandidateVo;
|
||||
import com.nanri.aiimage.modules.productrisk.model.vo.ProductRiskDashboardVo;
|
||||
import com.nanri.aiimage.modules.productrisk.model.vo.ProductRiskMatchShopsVo;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinCreateTaskRequest;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinSubmitResultRequest;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinTaskBatchRequest;
|
||||
@@ -9,11 +14,6 @@ import com.nanri.aiimage.modules.queryasin.model.vo.QueryAsinHistoryVo;
|
||||
import com.nanri.aiimage.modules.queryasin.model.vo.QueryAsinTaskBatchVo;
|
||||
import com.nanri.aiimage.modules.queryasin.service.QueryAsinResolveService;
|
||||
import com.nanri.aiimage.modules.queryasin.service.QueryAsinTaskService;
|
||||
import com.nanri.aiimage.modules.productrisk.model.dto.ProductRiskCandidateAddRequest;
|
||||
import com.nanri.aiimage.modules.productrisk.model.dto.ProductRiskMatchShopsRequest;
|
||||
import com.nanri.aiimage.modules.productrisk.model.vo.ProductRiskCandidateVo;
|
||||
import com.nanri.aiimage.modules.productrisk.model.vo.ProductRiskDashboardVo;
|
||||
import com.nanri.aiimage.modules.productrisk.model.vo.ProductRiskMatchShopsVo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
@@ -46,14 +46,14 @@ import java.util.List;
|
||||
@RequestMapping("/api/query-asin")
|
||||
@Tag(
|
||||
name = "查询ASIN",
|
||||
description = "查询ASIN模块:管理备选店铺、批量匹配紫鸟店铺、创建任务、接收 Python 分片结果、自动收尾生成 Excel 并提供下载。涉及查询和删除的接口需要携带 user_id。")
|
||||
description = "查询 ASIN 模块:管理备选店铺、批量匹配紫鸟店铺、创建任务、接收 Python 分片结果、自动收尾生成 Excel 并提供下载。查询和删除接口需要携带 user_id。")
|
||||
public class QueryAsinTaskController {
|
||||
|
||||
private final QueryAsinResolveService queryAsinResolveService;
|
||||
private final QueryAsinTaskService queryAsinTaskService;
|
||||
|
||||
@GetMapping("/candidates")
|
||||
@Operation(summary = "查询备选店铺列表", description = "返回当前用户在查询ASIN模块中已保存的备选店铺。")
|
||||
@Operation(summary = "查询备选店铺列表", description = "返回当前用户在查询 ASIN 模块中已保存的备选店铺。")
|
||||
public ApiResponse<List<ProductRiskCandidateVo>> listCandidates(
|
||||
@Parameter(name = "user_id", description = "当前用户 ID", required = true, in = ParameterIn.QUERY, example = "1")
|
||||
@RequestParam("user_id") Long userId) {
|
||||
@@ -61,10 +61,10 @@ public class QueryAsinTaskController {
|
||||
}
|
||||
|
||||
@PostMapping("/candidates")
|
||||
@Operation(summary = "新增备选店铺", description = "向查询ASIN备选区新增一条店铺记录,供后续匹配和创建任务使用。")
|
||||
@Operation(summary = "新增备选店铺", description = "向查询 ASIN 备选区新增一条店铺记录,供后续匹配和创建任务使用。")
|
||||
public ApiResponse<ProductRiskCandidateVo> addCandidate(
|
||||
@io.swagger.v3.oas.annotations.parameters.RequestBody(
|
||||
description = "新增备选店铺请求,需传入用户 ID 和店铺名。",
|
||||
description = "新增备选店铺请求,需要传入用户 ID 和店铺名。",
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = "application/json",
|
||||
@@ -93,7 +93,7 @@ public class QueryAsinTaskController {
|
||||
}
|
||||
|
||||
@PostMapping("/match-shops")
|
||||
@Operation(summary = "批量匹配店铺", description = "根据店铺名批量匹配紫鸟店铺索引,返回是否命中、店铺 ID、平台、公司和匹配状态。")
|
||||
@Operation(summary = "批量匹配店铺", description = "根据店铺名称批量匹配紫鸟店铺索引,返回是否命中、店铺 ID、平台、公司和匹配状态。")
|
||||
public ApiResponse<ProductRiskMatchShopsVo> matchShops(
|
||||
@io.swagger.v3.oas.annotations.parameters.RequestBody(
|
||||
description = "批量匹配请求,传入 user_id 和待匹配的店铺名称列表。",
|
||||
@@ -125,7 +125,7 @@ public class QueryAsinTaskController {
|
||||
}
|
||||
|
||||
@GetMapping("/history")
|
||||
@Operation(summary = "查询任务记录", description = "返回当前用户在查询ASIN模块中的当前任务和历史任务记录。")
|
||||
@Operation(summary = "查询任务记录", description = "返回当前用户在查询 ASIN 模块中的当前任务和历史任务记录。")
|
||||
public ApiResponse<QueryAsinHistoryVo> history(
|
||||
@Parameter(name = "user_id", description = "当前用户 ID", required = true, in = ParameterIn.QUERY, example = "1")
|
||||
@RequestParam("user_id") Long userId) {
|
||||
@@ -133,16 +133,16 @@ public class QueryAsinTaskController {
|
||||
}
|
||||
|
||||
@PostMapping("/tasks/progress/batch")
|
||||
@Operation(summary = "批量查询查询ASIN任务进度", description = "仅返回任务状态和店铺结果摘要,用于前端轮询降载。")
|
||||
@Operation(summary = "批量查询查询 ASIN 任务进度", description = "仅返回任务状态和店铺结果摘要,用于前端轮询降载。")
|
||||
public ApiResponse<QueryAsinTaskBatchVo> taskProgressBatch(@Valid @RequestBody QueryAsinTaskBatchRequest request) {
|
||||
return ApiResponse.success(queryAsinTaskService.getTaskProgressBatch(request.getTaskIds()));
|
||||
}
|
||||
|
||||
@PostMapping("/tasks")
|
||||
@Operation(summary = "创建查询ASIN任务", description = "根据已匹配的店铺创建任务和占位结果记录,后续由 Python 端处理并回传结果。")
|
||||
@Operation(summary = "创建查询 ASIN 任务", description = "根据已匹配的店铺创建任务和占位结果记录,并把后台维护的整张 ASIN 表数据随店铺项返回给 Python 端。")
|
||||
public ApiResponse<QueryAsinCreateTaskVo> createTask(
|
||||
@io.swagger.v3.oas.annotations.parameters.RequestBody(
|
||||
description = "创建任务请求。items 中每一项代表一个待处理店铺,需携带默认模板结构。",
|
||||
description = "创建任务请求。items 中每一项代表一个待处理店铺,queryAsins 是后台维护的整张 ASIN 表数据,不按店铺过滤。",
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = "application/json",
|
||||
@@ -161,23 +161,14 @@ public class QueryAsinTaskController {
|
||||
"companyName": "示例公司",
|
||||
"matchStatus": "MATCHED",
|
||||
"matchMessage": "索引已命中",
|
||||
"countrySections": [
|
||||
"queryAsins": [
|
||||
{
|
||||
"country": "德国",
|
||||
"rows": [
|
||||
{
|
||||
"status": "全部",
|
||||
"quantity": "",
|
||||
"deleteQuantity": "",
|
||||
"processStatus": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"cartRatios": [
|
||||
"country": "DE",
|
||||
"asins": ["B0BRZZR3N2", "B0BRZZR3N3"]
|
||||
},
|
||||
{
|
||||
"country": "德国",
|
||||
"ratio": ""
|
||||
"country": "FR",
|
||||
"asins": ["B0BRZZR3N4"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -190,124 +181,72 @@ public class QueryAsinTaskController {
|
||||
|
||||
@PostMapping("/tasks/{taskId}/result")
|
||||
@Operation(
|
||||
summary = "提交查询ASIN结果",
|
||||
description = "供 Python 端回传处理结果。支持按店铺分片多次提交,服务端会合并分片、按店铺完成状态自动收尾,并在任务结束后生成 Excel。")
|
||||
summary = "提交查询 ASIN 结果",
|
||||
description = "供 Python 端回传查询 ASIN 结果。业务数据包含店铺名,以及德国、英国、法国、意大利、西班牙 5 个国家下的 ASIN 和处理状态;submissionId 用于日志追踪,shopDone 可不传,不传时后端默认该店铺已提交完成。")
|
||||
public ApiResponse<Void> submitResult(
|
||||
@Parameter(description = "任务主键,必须是运行中的任务", example = "3089")
|
||||
@Parameter(description = "任务主键,必须是运行中的查询 ASIN 任务", example = "3089")
|
||||
@PathVariable Long taskId,
|
||||
@io.swagger.v3.oas.annotations.parameters.RequestBody(
|
||||
description = "查询ASIN结果回传请求。shops 表示本次提交的店铺结果分片列表。shopDone=true 表示该店铺已全部提交完成。",
|
||||
description = "查询 ASIN 结果回传请求。shops 表示店铺结果列表;countryResults 对应导出表中的国家 ASIN 列和处理状态列;error 用于回传单店铺失败原因。",
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = "application/json",
|
||||
schema = @Schema(implementation = QueryAsinSubmitResultRequest.class),
|
||||
examples = {
|
||||
@ExampleObject(
|
||||
name = "成功分片示例",
|
||||
summary = "一个店铺分片回传,包含状态数据和购物车比例数据",
|
||||
value = """
|
||||
examples = @ExampleObject(
|
||||
name = "回传示例",
|
||||
summary = "按店铺和国家返回 ASIN 与处理状态",
|
||||
value = """
|
||||
{
|
||||
"shops": [
|
||||
{
|
||||
"shopName": "郭亚芳",
|
||||
"submissionId": "query-asin:3089:郭亚芳:1711111111111",
|
||||
"shopDone": true,
|
||||
"countryResults": [
|
||||
{
|
||||
"shops": [
|
||||
"country": "DE",
|
||||
"items": [
|
||||
{
|
||||
"shopName": "郭亚芳",
|
||||
"submissionId": "query-asin:3089:郭亚芳:1711111111111",
|
||||
"chunkIndex": 1,
|
||||
"chunkTotal": 2,
|
||||
"shopDone": false,
|
||||
"countrySections": [
|
||||
{
|
||||
"country": "德国",
|
||||
"rows": [
|
||||
{
|
||||
"status": "正常",
|
||||
"quantity": "12",
|
||||
"deleteQuantity": "3",
|
||||
"processStatus": "处理中"
|
||||
},
|
||||
{
|
||||
"status": "下架",
|
||||
"quantity": "2",
|
||||
"deleteQuantity": "2",
|
||||
"processStatus": "已完成"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"country": "英国",
|
||||
"rows": [
|
||||
{
|
||||
"status": "正常",
|
||||
"quantity": "5",
|
||||
"deleteQuantity": "1",
|
||||
"processStatus": "处理中"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"cartRatios": [
|
||||
{
|
||||
"country": "德国",
|
||||
"ratio": "25%"
|
||||
},
|
||||
{
|
||||
"country": "英国",
|
||||
"ratio": "18%"
|
||||
}
|
||||
]
|
||||
"asin": "B0BRZZR3N2",
|
||||
"status": "正常"
|
||||
},
|
||||
{
|
||||
"asin": "B0BRZZR3N3",
|
||||
"status": "未找到"
|
||||
}
|
||||
]
|
||||
}
|
||||
"""),
|
||||
@ExampleObject(
|
||||
name = "店铺完成示例",
|
||||
summary = "最后一片提交完成后,将 shopDone 置为 true",
|
||||
value = """
|
||||
},
|
||||
{
|
||||
"shops": [
|
||||
"country": "UK",
|
||||
"items": [
|
||||
{
|
||||
"shopName": "郭亚芳",
|
||||
"submissionId": "query-asin:3089:郭亚芳:1711111111111",
|
||||
"chunkIndex": 2,
|
||||
"chunkTotal": 2,
|
||||
"shopDone": true,
|
||||
"countrySections": [
|
||||
{
|
||||
"country": "法国",
|
||||
"rows": [
|
||||
{
|
||||
"status": "正常",
|
||||
"quantity": "4",
|
||||
"deleteQuantity": "0",
|
||||
"processStatus": "已完成"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"cartRatios": [
|
||||
{
|
||||
"country": "法国",
|
||||
"ratio": "12%"
|
||||
}
|
||||
]
|
||||
"asin": "B0BRZZR3N6",
|
||||
"status": "正常"
|
||||
}
|
||||
]
|
||||
}
|
||||
"""),
|
||||
@ExampleObject(
|
||||
name = "失败示例",
|
||||
summary = "店铺处理失败时直接回传错误信息",
|
||||
value = """
|
||||
},
|
||||
{
|
||||
"shops": [
|
||||
"country": "FR",
|
||||
"items": [
|
||||
{
|
||||
"shopName": "郭亚芳",
|
||||
"shopDone": true,
|
||||
"error": "紫鸟页面加载超时,未能完成删除数据采集"
|
||||
"asin": "B0BRZZR3N4",
|
||||
"status": "正常"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"country": "IT",
|
||||
"items": []
|
||||
},
|
||||
{
|
||||
"country": "ES",
|
||||
"items": []
|
||||
}
|
||||
""")
|
||||
}))
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
""")))
|
||||
@Valid @RequestBody QueryAsinSubmitResultRequest request,
|
||||
jakarta.servlet.http.HttpServletResponse response) {
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
@@ -317,7 +256,7 @@ public class QueryAsinTaskController {
|
||||
}
|
||||
|
||||
@GetMapping("/results/{resultId}/download")
|
||||
@Operation(summary = "下载结果文件", description = "按结果记录下载服务端已生成并上传到 OSS 的查询ASIN Excel 文件。")
|
||||
@Operation(summary = "下载结果文件", description = "按结果记录下载服务端已生成并上传到 OSS 的查询 ASIN Excel 文件。")
|
||||
public void downloadResult(
|
||||
@Parameter(description = "结果记录主键", example = "4599")
|
||||
@PathVariable Long resultId,
|
||||
@@ -348,7 +287,7 @@ public class QueryAsinTaskController {
|
||||
}
|
||||
|
||||
@DeleteMapping("/tasks/{taskId}")
|
||||
@Operation(summary = "删除任务", description = "删除整条查询ASIN任务以及该任务下关联的所有结果记录。")
|
||||
@Operation(summary = "删除任务", description = "删除整条查询 ASIN 任务以及该任务下关联的所有结果记录。")
|
||||
public ApiResponse<Void> deleteTask(
|
||||
@Parameter(description = "任务主键", example = "3089")
|
||||
@PathVariable Long taskId,
|
||||
@@ -359,7 +298,7 @@ public class QueryAsinTaskController {
|
||||
}
|
||||
|
||||
@DeleteMapping("/history/{resultId}")
|
||||
@Operation(summary = "删除单条历史记录", description = "删除一条查询ASIN结果记录,并同步重算其所属任务状态。")
|
||||
@Operation(summary = "删除单条历史记录", description = "删除一条查询 ASIN 结果记录,并同步重算其所属任务状态。")
|
||||
public ApiResponse<Void> deleteHistory(
|
||||
@Parameter(description = "结果记录主键", example = "4599")
|
||||
@PathVariable Long resultId,
|
||||
@@ -369,6 +308,3 @@ public class QueryAsinTaskController {
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.nanri.aiimage.modules.queryasin.model.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "查询 ASIN 单条结果")
|
||||
public class QueryAsinAsinStatusDto {
|
||||
|
||||
@Schema(description = "ASIN", example = "B0BRZZR3N2")
|
||||
private String asin;
|
||||
|
||||
@Schema(description = "Python 查询后返回的状态", example = "正常")
|
||||
private String status;
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
package com.nanri.aiimage.modules.queryasin.model.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "购物车比例数据")
|
||||
public class QueryAsinCartRatioDto {
|
||||
|
||||
@Schema(description = "国家名称", example = "德国")
|
||||
private String country;
|
||||
|
||||
@Schema(description = "购物车比例", example = "25%")
|
||||
private String ratio;
|
||||
}
|
||||
|
||||
+2
-2
@@ -7,12 +7,12 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "查询 ASIN 国家维度 ASIN 列表")
|
||||
@Schema(description = "查询 ASIN 国家维度 ASIN 清单")
|
||||
public class QueryAsinCountryAsinsDto {
|
||||
|
||||
@Schema(description = "国家编码,支持 DE、UK、FR、IT、ES", example = "DE")
|
||||
private String country;
|
||||
|
||||
@Schema(description = "该国家需要查询的 ASIN 列表")
|
||||
@Schema(description = "后台维护的该国家 ASIN 清单,创建任务时会整体推给 Python")
|
||||
private List<String> asins = new ArrayList<>();
|
||||
}
|
||||
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
package com.nanri.aiimage.modules.queryasin.model.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "国家状态数据中的单行记录")
|
||||
public class QueryAsinCountryMetricRowDto {
|
||||
|
||||
@Schema(description = "商品状态", example = "正常")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "该状态下的商品数量", example = "12")
|
||||
private String quantity;
|
||||
|
||||
@JsonProperty("deleteQuantity")
|
||||
@Schema(description = "已删除数量", example = "3")
|
||||
private String deleteQuantity;
|
||||
|
||||
@JsonProperty("processStatus")
|
||||
@Schema(description = "处理结果或处理状态", example = "处理中")
|
||||
private String processStatus;
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.nanri.aiimage.modules.queryasin.model.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "查询 ASIN 国家维度结果")
|
||||
public class QueryAsinCountryResultDto {
|
||||
|
||||
@Schema(description = "国家编码,支持 DE、UK、FR、IT、ES", example = "DE")
|
||||
private String country;
|
||||
|
||||
@Schema(description = "该国家下的 ASIN 查询结果列表")
|
||||
private List<QueryAsinAsinStatusDto> items = new ArrayList<>();
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package com.nanri.aiimage.modules.queryasin.model.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "单个国家的状态数据分组")
|
||||
public class QueryAsinCountrySectionDto {
|
||||
|
||||
@Schema(description = "国家名称", example = "德国")
|
||||
private String country;
|
||||
|
||||
@Schema(description = "该国家下的多行状态数据")
|
||||
private List<QueryAsinCountryMetricRowDto> rows = new ArrayList<>();
|
||||
}
|
||||
|
||||
+8
-24
@@ -8,42 +8,26 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "单个店铺的查询ASIN结果分片")
|
||||
@Schema(description = "单个店铺的查询 ASIN 结果")
|
||||
public class QueryAsinShopPayloadDto {
|
||||
|
||||
@JsonProperty("shopName")
|
||||
@Schema(description = "店铺名称,服务端按店铺名称聚合分片", example = "郭亚芳")
|
||||
@Schema(description = "店铺名称", example = "郭亚芳")
|
||||
private String shopName;
|
||||
|
||||
@Schema(description = "店铺处理失败时的错误信息;有值时该店铺会直接标记失败", example = "紫鸟页面加载超时,未能完成删除数据采集")
|
||||
@Schema(description = "店铺处理失败时的错误信息;有值时该店铺会直接标记失败", example = "紫鸟页面加载超时,未能完成 ASIN 查询")
|
||||
private String error;
|
||||
|
||||
@JsonProperty("countrySections")
|
||||
@Schema(description = "各国家的状态数据列表。同一国家可分多片提交,服务端会按顺序合并")
|
||||
private List<QueryAsinCountrySectionDto> countrySections = new ArrayList<>();
|
||||
|
||||
@JsonProperty("cartRatios")
|
||||
@Schema(description = "购物车比例数据列表,通常只在店铺首行展示")
|
||||
private List<QueryAsinCartRatioDto> cartRatios = new ArrayList<>();
|
||||
|
||||
@JsonProperty("queryAsins")
|
||||
@Schema(description = "Python 回传的查询 ASIN 结果分片;如果 Python 仅透传后台 ASIN,也按该字段提交")
|
||||
private List<QueryAsinCountryAsinsDto> queryAsins = new ArrayList<>();
|
||||
@JsonProperty("countryResults")
|
||||
@Schema(description = "Python 回传的查询 ASIN 结果。每个国家下按 ASIN + 状态返回,对应导出表中的国家列和状态列")
|
||||
private List<QueryAsinCountryResultDto> countryResults = new ArrayList<>();
|
||||
|
||||
@JsonProperty("shopDone")
|
||||
@Schema(description = "该店铺是否已全部提交完成。最后一片应传 true", example = "true")
|
||||
@Schema(description = "该店铺是否已全部提交完成。不传时按已完成处理;分片提交时,中间分片传 false,最后一片传 true", example = "true")
|
||||
private Boolean shopDone;
|
||||
|
||||
@JsonProperty("submissionId")
|
||||
@Schema(description = "本次店铺提交批次标识,便于问题排查与日志跟踪", example = "query-asin:3089:郭亚芳:1711111111111")
|
||||
@Schema(description = "本次店铺提交批次标识,便于问题排查与日志追踪", example = "query-asin:3089:郭亚芳:1711111111111")
|
||||
private String submissionId;
|
||||
|
||||
@JsonProperty("chunkIndex")
|
||||
@Schema(description = "当前分片序号,建议从 1 开始", example = "1")
|
||||
private Integer chunkIndex;
|
||||
|
||||
@JsonProperty("chunkTotal")
|
||||
@Schema(description = "当前店铺总分片数", example = "2")
|
||||
private Integer chunkTotal;
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -9,7 +9,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "查询ASIN结果回传请求")
|
||||
@Schema(description = "查询 ASIN 结果回传请求")
|
||||
public class QueryAsinSubmitResultRequest {
|
||||
|
||||
@Valid
|
||||
@@ -17,4 +17,3 @@ public class QueryAsinSubmitResultRequest {
|
||||
@Schema(description = "本次提交的店铺结果分片列表,支持一次提交多个店铺", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<QueryAsinShopPayloadDto> shops = new ArrayList<>();
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "查询ASIN任务中的单个店铺项")
|
||||
@Schema(description = "查询 ASIN 任务中的单个店铺项")
|
||||
public class QueryAsinTaskItemDto {
|
||||
|
||||
@JsonProperty("shopName")
|
||||
@@ -40,6 +40,6 @@ public class QueryAsinTaskItemDto {
|
||||
|
||||
@Valid
|
||||
@JsonProperty("queryAsins")
|
||||
@Schema(description = "后台查询 ASIN 维护表中的国家 ASIN 列表;查询ASIN任务按全表返回,不按店铺过滤")
|
||||
@Schema(description = "后台查询 ASIN 维护表中的国家 ASIN 清单;查询 ASIN 任务按全表返回,不按店铺过滤")
|
||||
private List<QueryAsinCountryAsinsDto> queryAsins = new ArrayList<>();
|
||||
}
|
||||
|
||||
+4
-9
@@ -1,9 +1,8 @@
|
||||
package com.nanri.aiimage.modules.queryasin.model.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinCartRatioDto;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinCountryAsinsDto;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinCountrySectionDto;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinCountryResultDto;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
@@ -45,13 +44,9 @@ public class QueryAsinResultItemVo {
|
||||
private String outputFilename;
|
||||
private String downloadUrl;
|
||||
|
||||
@JsonProperty("countrySections")
|
||||
private List<QueryAsinCountrySectionDto> countrySections = new ArrayList<>();
|
||||
|
||||
@JsonProperty("cartRatios")
|
||||
private List<QueryAsinCartRatioDto> cartRatios = new ArrayList<>();
|
||||
|
||||
@JsonProperty("queryAsins")
|
||||
private List<QueryAsinCountryAsinsDto> queryAsins = new ArrayList<>();
|
||||
}
|
||||
|
||||
@JsonProperty("countryResults")
|
||||
private List<QueryAsinCountryResultDto> countryResults = new ArrayList<>();
|
||||
}
|
||||
|
||||
+107
-23
@@ -1,7 +1,9 @@
|
||||
package com.nanri.aiimage.modules.queryasin.service;
|
||||
|
||||
import com.nanri.aiimage.common.exception.BusinessException;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinAsinStatusDto;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinCountryAsinsDto;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinCountryResultDto;
|
||||
import com.nanri.aiimage.modules.queryasin.model.vo.QueryAsinResultItemVo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
@@ -11,6 +13,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -20,7 +23,21 @@ import java.util.Map;
|
||||
public class QueryAsinExcelAssemblyService {
|
||||
|
||||
private static final List<String> COUNTRIES = List.of("DE", "UK", "FR", "IT", "ES");
|
||||
private static final String[] HEADER = {"店铺名", "德国", "英国", "法国", "意大利", "西班牙"};
|
||||
private static final Map<String, String> COUNTRY_NAMES = Map.of(
|
||||
"DE", "德国",
|
||||
"UK", "英国",
|
||||
"FR", "法国",
|
||||
"IT", "意大利",
|
||||
"ES", "西班牙"
|
||||
);
|
||||
private static final String[] HEADER = {
|
||||
"店铺名",
|
||||
"德国", "状态",
|
||||
"英国", "状态",
|
||||
"法国", "状态",
|
||||
"意大利", "状态",
|
||||
"西班牙", "状态"
|
||||
};
|
||||
|
||||
public void writeWorkbook(File outputXlsx, List<QueryAsinResultItemVo> items) {
|
||||
SXSSFWorkbook workbook = new SXSSFWorkbook(200);
|
||||
@@ -38,14 +55,17 @@ public class QueryAsinExcelAssemblyService {
|
||||
if (item == null || Boolean.FALSE.equals(item.getSuccess())) {
|
||||
continue;
|
||||
}
|
||||
Map<String, List<String>> asinsByCountry = asinsByCountry(item.getQueryAsins());
|
||||
int maxRows = maxRows(asinsByCountry);
|
||||
Map<String, List<QueryAsinAsinStatusDto>> resultsByCountry = resultsByCountry(item);
|
||||
int maxRows = maxRows(resultsByCountry);
|
||||
for (int i = 0; i < maxRows; i++) {
|
||||
Row row = sheet.createRow(rowIndex++);
|
||||
row.createCell(0).setCellValue(i == 0 ? safe(item.getShopName()) : "");
|
||||
for (int c = 0; c < COUNTRIES.size(); c++) {
|
||||
List<String> asins = asinsByCountry.get(COUNTRIES.get(c));
|
||||
row.createCell(c + 1).setCellValue(asins != null && i < asins.size() ? safe(asins.get(i)) : "");
|
||||
List<QueryAsinAsinStatusDto> rows = resultsByCountry.get(COUNTRIES.get(c));
|
||||
QueryAsinAsinStatusDto result = rows != null && i < rows.size() ? rows.get(i) : null;
|
||||
int asinColumn = 1 + c * 2;
|
||||
row.createCell(asinColumn).setCellValue(result == null ? "" : safe(result.getAsin()));
|
||||
row.createCell(asinColumn + 1).setCellValue(result == null ? "" : safe(result.getStatus()));
|
||||
}
|
||||
}
|
||||
rowIndex++;
|
||||
@@ -53,7 +73,7 @@ public class QueryAsinExcelAssemblyService {
|
||||
workbook.write(outputStream);
|
||||
} catch (Exception ex) {
|
||||
log.warn("[query-asin] write workbook failed: {}", ex.getMessage());
|
||||
throw new BusinessException("generate query-asin excel failed: " + ex.getMessage());
|
||||
throw new BusinessException("生成查询 ASIN Excel 失败: " + ex.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
workbook.close();
|
||||
@@ -70,42 +90,106 @@ public class QueryAsinExcelAssemblyService {
|
||||
}
|
||||
for (QueryAsinResultItemVo item : items) {
|
||||
if (item != null && !Boolean.FALSE.equals(item.getSuccess())) {
|
||||
count += maxRows(asinsByCountry(item.getQueryAsins()));
|
||||
count += maxRows(resultsByCountry(item));
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private Map<String, List<String>> asinsByCountry(List<QueryAsinCountryAsinsDto> queryAsins) {
|
||||
Map<String, List<String>> map = new LinkedHashMap<>();
|
||||
for (String country : COUNTRIES) {
|
||||
map.put(country, List.of());
|
||||
}
|
||||
if (queryAsins == null) {
|
||||
private Map<String, List<QueryAsinAsinStatusDto>> resultsByCountry(QueryAsinResultItemVo item) {
|
||||
Map<String, List<QueryAsinAsinStatusDto>> map = emptyCountryMap();
|
||||
if (item == null) {
|
||||
return map;
|
||||
}
|
||||
for (QueryAsinCountryAsinsDto item : queryAsins) {
|
||||
if (item == null || item.getCountry() == null) {
|
||||
continue;
|
||||
if (item.getCountryResults() != null && !item.getCountryResults().isEmpty()) {
|
||||
for (QueryAsinCountryResultDto countryResult : item.getCountryResults()) {
|
||||
String country = normalizeCountry(countryResult == null ? null : countryResult.getCountry());
|
||||
if (!map.containsKey(country)) {
|
||||
continue;
|
||||
}
|
||||
map.put(country, normalizeResultItems(countryResult.getItems()));
|
||||
}
|
||||
String country = item.getCountry().trim().toUpperCase();
|
||||
if (map.containsKey(country)) {
|
||||
map.put(country, item.getAsins() == null ? List.of() : item.getAsins());
|
||||
return map;
|
||||
}
|
||||
|
||||
// 兼容旧链路:如果 Python 仍然只透传 queryAsins,则状态列留空。
|
||||
if (item.getQueryAsins() != null) {
|
||||
for (QueryAsinCountryAsinsDto countryAsins : item.getQueryAsins()) {
|
||||
String country = normalizeCountry(countryAsins == null ? null : countryAsins.getCountry());
|
||||
if (!map.containsKey(country)) {
|
||||
continue;
|
||||
}
|
||||
List<QueryAsinAsinStatusDto> rows = new ArrayList<>();
|
||||
for (String asin : countryAsins.getAsins() == null ? List.<String>of() : countryAsins.getAsins()) {
|
||||
String normalizedAsin = normalizeAsin(asin);
|
||||
if (normalizedAsin.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
QueryAsinAsinStatusDto row = new QueryAsinAsinStatusDto();
|
||||
row.setAsin(normalizedAsin);
|
||||
row.setStatus("");
|
||||
rows.add(row);
|
||||
}
|
||||
map.put(country, rows);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private int maxRows(Map<String, List<String>> asinsByCountry) {
|
||||
private Map<String, List<QueryAsinAsinStatusDto>> emptyCountryMap() {
|
||||
Map<String, List<QueryAsinAsinStatusDto>> map = new LinkedHashMap<>();
|
||||
for (String country : COUNTRIES) {
|
||||
map.put(country, List.of());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private List<QueryAsinAsinStatusDto> normalizeResultItems(List<QueryAsinAsinStatusDto> items) {
|
||||
List<QueryAsinAsinStatusDto> out = new ArrayList<>();
|
||||
if (items == null) {
|
||||
return out;
|
||||
}
|
||||
for (QueryAsinAsinStatusDto source : items) {
|
||||
String asin = normalizeAsin(source == null ? null : source.getAsin());
|
||||
String status = source == null ? "" : safe(source.getStatus()).trim();
|
||||
if (asin.isEmpty() && status.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
QueryAsinAsinStatusDto item = new QueryAsinAsinStatusDto();
|
||||
item.setAsin(asin);
|
||||
item.setStatus(status);
|
||||
out.add(item);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private int maxRows(Map<String, List<QueryAsinAsinStatusDto>> resultsByCountry) {
|
||||
int max = 1;
|
||||
for (List<String> asins : asinsByCountry.values()) {
|
||||
if (asins != null && asins.size() > max) {
|
||||
max = asins.size();
|
||||
for (List<QueryAsinAsinStatusDto> rows : resultsByCountry.values()) {
|
||||
if (rows != null && rows.size() > max) {
|
||||
max = rows.size();
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
private String normalizeCountry(String country) {
|
||||
if (country == null) {
|
||||
return "";
|
||||
}
|
||||
String value = country.trim().toUpperCase();
|
||||
for (Map.Entry<String, String> entry : COUNTRY_NAMES.entrySet()) {
|
||||
if (entry.getValue().equals(country.trim())) {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private String normalizeAsin(String value) {
|
||||
return value == null ? "" : value.trim().toUpperCase();
|
||||
}
|
||||
|
||||
private String safe(String value) {
|
||||
return value == null ? "" : value;
|
||||
}
|
||||
|
||||
+89
-102
@@ -8,10 +8,9 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.nanri.aiimage.common.exception.BusinessException;
|
||||
import com.nanri.aiimage.config.TaskPressureProperties;
|
||||
import com.nanri.aiimage.modules.file.service.oss.OssStorageService;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinCartRatioDto;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinAsinStatusDto;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinCountryAsinsDto;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinCountryMetricRowDto;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinCountrySectionDto;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinCountryResultDto;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinCreateTaskRequest;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinShopPayloadDto;
|
||||
import com.nanri.aiimage.modules.queryasin.model.dto.QueryAsinSubmitResultRequest;
|
||||
@@ -523,11 +522,8 @@ public class QueryAsinTaskService {
|
||||
item.setDownloadUrl(blank(entity.getResultFileUrl())
|
||||
? item.getDownloadUrl()
|
||||
: ossStorageService.generateFreshDownloadUrl(entity.getResultFileUrl()));
|
||||
if (item.getCountrySections() == null) {
|
||||
item.setCountrySections(new ArrayList<>());
|
||||
}
|
||||
if (item.getCartRatios() == null) {
|
||||
item.setCartRatios(new ArrayList<>());
|
||||
if (item.getCountryResults() == null) {
|
||||
item.setCountryResults(new ArrayList<>());
|
||||
}
|
||||
if (item.getQueryAsins() == null) {
|
||||
item.setQueryAsins(new ArrayList<>());
|
||||
@@ -568,8 +564,7 @@ public class QueryAsinTaskService {
|
||||
vo.setError(result.getErrorMessage());
|
||||
vo.setCreatedAt(result.getCreatedAt());
|
||||
vo.setFinishedAt(finishedAt);
|
||||
vo.setCountrySections(new ArrayList<>());
|
||||
vo.setCartRatios(new ArrayList<>());
|
||||
vo.setCountryResults(new ArrayList<>());
|
||||
vo.setQueryAsins(copyCountryAsins(item.getQueryAsins()));
|
||||
vo.setOutputFilename(result.getResultFilename());
|
||||
vo.setDownloadUrl(null);
|
||||
@@ -642,15 +637,11 @@ public class QueryAsinTaskService {
|
||||
if (merged == null) {
|
||||
merged = new QueryAsinShopPayloadDto();
|
||||
merged.setShopName(shopKey);
|
||||
merged.setCountrySections(new ArrayList<>());
|
||||
merged.setCartRatios(new ArrayList<>());
|
||||
merged.setQueryAsins(new ArrayList<>());
|
||||
merged.setCountryResults(new ArrayList<>());
|
||||
}
|
||||
|
||||
merged.setShopName(firstNonBlank(incoming.getShopName(), merged.getShopName()));
|
||||
merged.setSubmissionId(firstNonBlank(incoming.getSubmissionId(), merged.getSubmissionId()));
|
||||
merged.setChunkIndex(incoming.getChunkIndex());
|
||||
merged.setChunkTotal(incoming.getChunkTotal());
|
||||
|
||||
if (!blank(incoming.getError())) {
|
||||
merged.setError(incoming.getError().trim());
|
||||
@@ -659,10 +650,8 @@ public class QueryAsinTaskService {
|
||||
return merged;
|
||||
}
|
||||
|
||||
merged.setCountrySections(mergeCountrySections(merged.getCountrySections(), incoming.getCountrySections()));
|
||||
merged.setCartRatios(mergeCartRatios(merged.getCartRatios(), incoming.getCartRatios()));
|
||||
merged.setQueryAsins(mergeCountryAsins(merged.getQueryAsins(), incoming.getQueryAsins()));
|
||||
if (Boolean.TRUE.equals(incoming.getShopDone())) {
|
||||
merged.setCountryResults(mergeCountryResults(merged.getCountryResults(), incoming.getCountryResults()));
|
||||
if (incoming.getShopDone() == null || Boolean.TRUE.equals(incoming.getShopDone())) {
|
||||
merged.setShopDone(Boolean.TRUE);
|
||||
}
|
||||
taskCacheService.saveShopMergedPayload(taskId, shopKey, merged);
|
||||
@@ -674,11 +663,7 @@ public class QueryAsinTaskService {
|
||||
return;
|
||||
}
|
||||
snapshot.setShopName(firstNonBlank(payload.getShopName(), snapshot.getShopName()));
|
||||
snapshot.setCountrySections(copyCountrySections(payload.getCountrySections()));
|
||||
snapshot.setCartRatios(copyCartRatios(payload.getCartRatios()));
|
||||
if (payload.getQueryAsins() != null && !payload.getQueryAsins().isEmpty()) {
|
||||
snapshot.setQueryAsins(copyCountryAsins(payload.getQueryAsins()));
|
||||
}
|
||||
snapshot.setCountryResults(copyCountryResults(payload.getCountryResults()));
|
||||
if (!blank(payload.getError())) {
|
||||
snapshot.setError(payload.getError().trim());
|
||||
}
|
||||
@@ -700,37 +685,38 @@ public class QueryAsinTaskService {
|
||||
snapshot.setError(blankToNull(error));
|
||||
}
|
||||
|
||||
private List<QueryAsinCountrySectionDto> mergeCountrySections(List<QueryAsinCountrySectionDto> base, List<QueryAsinCountrySectionDto> incoming) {
|
||||
Map<String, QueryAsinCountrySectionDto> map = new LinkedHashMap<>();
|
||||
for (QueryAsinCountrySectionDto section : copyCountrySections(base)) {
|
||||
map.put(section.getCountry(), section);
|
||||
private List<QueryAsinCountryResultDto> mergeCountryResults(List<QueryAsinCountryResultDto> base, List<QueryAsinCountryResultDto> incoming) {
|
||||
Map<String, QueryAsinCountryResultDto> map = new LinkedHashMap<>();
|
||||
for (QueryAsinCountryResultDto item : copyCountryResults(base)) {
|
||||
map.put(item.getCountry(), item);
|
||||
}
|
||||
for (QueryAsinCountrySectionDto section : copyCountrySections(incoming)) {
|
||||
QueryAsinCountrySectionDto existing = map.get(section.getCountry());
|
||||
if (existing == null) {
|
||||
map.put(section.getCountry(), section);
|
||||
for (QueryAsinCountryResultDto item : copyCountryResults(incoming)) {
|
||||
if (blank(item.getCountry())) {
|
||||
continue;
|
||||
}
|
||||
List<QueryAsinCountryMetricRowDto> mergedRows = existing.getRows() == null
|
||||
? new ArrayList<>()
|
||||
: new ArrayList<>(existing.getRows());
|
||||
if (section.getRows() != null) {
|
||||
mergedRows.addAll(section.getRows());
|
||||
QueryAsinCountryResultDto existing = map.get(item.getCountry());
|
||||
if (existing == null) {
|
||||
map.put(item.getCountry(), item);
|
||||
continue;
|
||||
}
|
||||
existing.setRows(mergedRows);
|
||||
}
|
||||
return new ArrayList<>(map.values());
|
||||
}
|
||||
|
||||
private List<QueryAsinCartRatioDto> mergeCartRatios(List<QueryAsinCartRatioDto> base, List<QueryAsinCartRatioDto> incoming) {
|
||||
Map<String, QueryAsinCartRatioDto> map = new LinkedHashMap<>();
|
||||
for (QueryAsinCartRatioDto ratio : copyCartRatios(base)) {
|
||||
map.put(ratio.getCountry(), ratio);
|
||||
}
|
||||
for (QueryAsinCartRatioDto ratio : copyCartRatios(incoming)) {
|
||||
if (!blank(ratio.getCountry())) {
|
||||
map.put(ratio.getCountry(), ratio);
|
||||
List<QueryAsinAsinStatusDto> merged = new ArrayList<>(existing.getItems() == null ? List.of() : existing.getItems());
|
||||
for (QueryAsinAsinStatusDto row : item.getItems() == null ? List.<QueryAsinAsinStatusDto>of() : item.getItems()) {
|
||||
String asin = normalizeAsin(row == null ? null : row.getAsin());
|
||||
String status = row == null ? "" : firstNonBlank(row.getStatus(), "").trim();
|
||||
if (asin.isEmpty() && status.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
boolean exists = merged.stream().anyMatch(old ->
|
||||
Objects.equals(normalizeAsin(old.getAsin()), asin)
|
||||
&& Objects.equals(firstNonBlank(old.getStatus(), "").trim(), status));
|
||||
if (!exists) {
|
||||
QueryAsinAsinStatusDto normalized = new QueryAsinAsinStatusDto();
|
||||
normalized.setAsin(asin);
|
||||
normalized.setStatus(status);
|
||||
merged.add(normalized);
|
||||
}
|
||||
}
|
||||
existing.setItems(merged);
|
||||
}
|
||||
return new ArrayList<>(map.values());
|
||||
}
|
||||
@@ -741,17 +727,19 @@ public class QueryAsinTaskService {
|
||||
map.put(item.getCountry(), item);
|
||||
}
|
||||
for (QueryAsinCountryAsinsDto item : copyCountryAsins(incoming)) {
|
||||
if (blank(item.getCountry())) {
|
||||
String country = normalizeCountry(item.getCountry());
|
||||
if (blank(country)) {
|
||||
continue;
|
||||
}
|
||||
QueryAsinCountryAsinsDto existing = map.get(item.getCountry());
|
||||
item.setCountry(country);
|
||||
QueryAsinCountryAsinsDto existing = map.get(country);
|
||||
if (existing == null) {
|
||||
map.put(item.getCountry(), item);
|
||||
map.put(country, item);
|
||||
continue;
|
||||
}
|
||||
List<String> merged = new ArrayList<>(existing.getAsins() == null ? List.of() : existing.getAsins());
|
||||
for (String asin : item.getAsins() == null ? List.<String>of() : item.getAsins()) {
|
||||
String normalized = asin == null ? "" : asin.trim().toUpperCase();
|
||||
String normalized = normalizeAsin(asin);
|
||||
if (!normalized.isEmpty() && !merged.contains(normalized)) {
|
||||
merged.add(normalized);
|
||||
}
|
||||
@@ -765,35 +753,18 @@ public class QueryAsinTaskService {
|
||||
if (payload == null) {
|
||||
return false;
|
||||
}
|
||||
if (payload.getQueryAsins() != null) {
|
||||
for (QueryAsinCountryAsinsDto item : payload.getQueryAsins()) {
|
||||
if (item != null && item.getAsins() != null && !item.getAsins().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (payload.getCountrySections() != null) {
|
||||
for (QueryAsinCountrySectionDto section : payload.getCountrySections()) {
|
||||
if (section == null || section.getRows() == null) {
|
||||
if (payload.getCountryResults() != null) {
|
||||
for (QueryAsinCountryResultDto countryResult : payload.getCountryResults()) {
|
||||
if (countryResult == null || countryResult.getItems() == null) {
|
||||
continue;
|
||||
}
|
||||
for (QueryAsinCountryMetricRowDto row : section.getRows()) {
|
||||
if (row != null && (!blank(row.getStatus())
|
||||
|| !blank(row.getQuantity())
|
||||
|| !blank(row.getDeleteQuantity())
|
||||
|| !blank(row.getProcessStatus()))) {
|
||||
for (QueryAsinAsinStatusDto item : countryResult.getItems()) {
|
||||
if (item != null && (!blank(item.getAsin()) || !blank(item.getStatus()))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (payload.getCartRatios() != null) {
|
||||
for (QueryAsinCartRatioDto ratio : payload.getCartRatios()) {
|
||||
if (ratio != null && (!blank(ratio.getCountry()) || !blank(ratio.getRatio()))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -872,35 +843,32 @@ public class QueryAsinTaskService {
|
||||
}
|
||||
}
|
||||
|
||||
private List<QueryAsinCountrySectionDto> copyCountrySections(List<QueryAsinCountrySectionDto> sections) {
|
||||
List<QueryAsinCountrySectionDto> copy = new ArrayList<>();
|
||||
if (sections == null) {
|
||||
private List<QueryAsinCountryResultDto> copyCountryResults(List<QueryAsinCountryResultDto> results) {
|
||||
List<QueryAsinCountryResultDto> copy = new ArrayList<>();
|
||||
if (results == null) {
|
||||
return copy;
|
||||
}
|
||||
for (QueryAsinCountrySectionDto section : sections) {
|
||||
if (section == null) {
|
||||
for (QueryAsinCountryResultDto source : results) {
|
||||
if (source == null || blank(source.getCountry())) {
|
||||
continue;
|
||||
}
|
||||
QueryAsinCountrySectionDto item = new QueryAsinCountrySectionDto();
|
||||
item.setCountry(section.getCountry());
|
||||
item.setRows(section.getRows() == null ? new ArrayList<>() : new ArrayList<>(section.getRows()));
|
||||
copy.add(item);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
private List<QueryAsinCartRatioDto> copyCartRatios(List<QueryAsinCartRatioDto> ratios) {
|
||||
List<QueryAsinCartRatioDto> copy = new ArrayList<>();
|
||||
if (ratios == null) {
|
||||
return copy;
|
||||
}
|
||||
for (QueryAsinCartRatioDto ratio : ratios) {
|
||||
if (ratio == null) {
|
||||
continue;
|
||||
QueryAsinCountryResultDto item = new QueryAsinCountryResultDto();
|
||||
item.setCountry(normalizeCountry(source.getCountry()));
|
||||
List<QueryAsinAsinStatusDto> rows = new ArrayList<>();
|
||||
if (source.getItems() != null) {
|
||||
for (QueryAsinAsinStatusDto sourceRow : source.getItems()) {
|
||||
String asin = normalizeAsin(sourceRow == null ? null : sourceRow.getAsin());
|
||||
String status = sourceRow == null ? "" : firstNonBlank(sourceRow.getStatus(), "").trim();
|
||||
if (asin.isEmpty() && status.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
QueryAsinAsinStatusDto row = new QueryAsinAsinStatusDto();
|
||||
row.setAsin(asin);
|
||||
row.setStatus(status);
|
||||
rows.add(row);
|
||||
}
|
||||
}
|
||||
QueryAsinCartRatioDto item = new QueryAsinCartRatioDto();
|
||||
item.setCountry(ratio.getCountry());
|
||||
item.setRatio(ratio.getRatio());
|
||||
item.setItems(rows);
|
||||
copy.add(item);
|
||||
}
|
||||
return copy;
|
||||
@@ -916,11 +884,11 @@ public class QueryAsinTaskService {
|
||||
continue;
|
||||
}
|
||||
QueryAsinCountryAsinsDto item = new QueryAsinCountryAsinsDto();
|
||||
item.setCountry(source.getCountry().trim().toUpperCase());
|
||||
item.setCountry(normalizeCountry(source.getCountry()));
|
||||
List<String> asins = new ArrayList<>();
|
||||
if (source.getAsins() != null) {
|
||||
for (String asin : source.getAsins()) {
|
||||
String normalized = asin == null ? "" : asin.trim().toUpperCase();
|
||||
String normalized = normalizeAsin(asin);
|
||||
if (!normalized.isEmpty() && !asins.contains(normalized)) {
|
||||
asins.add(normalized);
|
||||
}
|
||||
@@ -936,6 +904,25 @@ public class QueryAsinTaskService {
|
||||
return !blank(first) ? first : second;
|
||||
}
|
||||
|
||||
private String normalizeCountry(String country) {
|
||||
if (country == null) {
|
||||
return "";
|
||||
}
|
||||
String value = country.trim().toUpperCase();
|
||||
return switch (value) {
|
||||
case "德国" -> "DE";
|
||||
case "英国" -> "UK";
|
||||
case "法国" -> "FR";
|
||||
case "意大利" -> "IT";
|
||||
case "西班牙" -> "ES";
|
||||
default -> value;
|
||||
};
|
||||
}
|
||||
|
||||
private String normalizeAsin(String asin) {
|
||||
return asin == null ? "" : asin.trim().toUpperCase();
|
||||
}
|
||||
|
||||
private String safeFileStem(String value) {
|
||||
String raw = value == null ? "result" : value.trim();
|
||||
String safe = raw.replaceAll("[\\\\/:*?\"<>|]+", "_");
|
||||
|
||||
Reference in New Issue
Block a user