软件前端更新
This commit is contained in:
@@ -156,9 +156,6 @@ public class SimilarAsinCozeClient {
|
||||
private InspectAttempt inspectOnce(List<SimilarAsinResultRowDto> rows, String prompt, String apiKey) throws Exception {
|
||||
String raw = runWorkflowAsyncAndWait(rows, prompt, apiKey);
|
||||
List<CozeResult> results = parseResults(raw);
|
||||
if (rows.size() > 1 && !results.isEmpty() && results.stream().noneMatch(this::hasIdentity)) {
|
||||
throw new PartialCozeResultException(0, rows.size(), results.size());
|
||||
}
|
||||
List<SimilarAsinResultRowDto> merged = mergeRows(rows, results);
|
||||
return new InspectAttempt(raw, merged, resolvedCount(merged), results.size());
|
||||
}
|
||||
@@ -259,18 +256,14 @@ public class SimilarAsinCozeClient {
|
||||
}
|
||||
|
||||
private Map<String, Object> buildParameters(List<SimilarAsinResultRowDto> rows, String prompt, String apiKey) {
|
||||
List<String> groupKeys = rows.stream().map(row -> nonBlank(row.getGroupKey(), rowKey(row.getId(), row.getAsin(), row.getCountry()))).toList();
|
||||
List<String> rowTokens = rows.stream().map(row -> nonBlank(row.getRowToken(), "")).toList();
|
||||
List<String> rowIds = rows.stream().map(row -> nonBlank(row.getId(), "")).toList();
|
||||
List<String> asins = rows.stream().map(row -> nonBlank(row.getAsin(), "")).toList();
|
||||
List<String> countries = rows.stream().map(row -> nonBlank(row.getCountry(), "")).toList();
|
||||
List<String> skus = rows.stream().map(row -> nonBlank(row.getSku(), "")).toList();
|
||||
List<String> titles = rows.stream().map(row -> nonBlank(row.getTitle(), row.getAsin())).toList();
|
||||
List<String> urls = rows.stream().map(row -> nonBlank(row.getUrl(), "")).toList();
|
||||
List<List<String>> urlLists = rows.stream().map(SimilarAsinResultRowDto::getUrls).toList();
|
||||
|
||||
Map<String, Object> parameters = new LinkedHashMap<>();
|
||||
parameters.put("items", buildItemObjects(groupKeys, rowTokens, rowIds, asins, countries, skus, titles, urls, urlLists));
|
||||
parameters.put("items", buildItemObjects(asins, skus, titles, urls, urlLists));
|
||||
parameters.put("prompt", prompt == null ? "" : prompt);
|
||||
if (apiKey != null && !apiKey.isBlank()) {
|
||||
parameters.put("api_key", apiKey.trim());
|
||||
@@ -308,11 +301,7 @@ public class SimilarAsinCozeClient {
|
||||
return normalized.substring(0, 6) + "***" + normalized.substring(normalized.length() - 4);
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> buildItemObjects(List<String> groupKeys,
|
||||
List<String> rowTokens,
|
||||
List<String> rowIds,
|
||||
List<String> asins,
|
||||
List<String> countries,
|
||||
private List<Map<String, Object>> buildItemObjects(List<String> asins,
|
||||
List<String> skus,
|
||||
List<String> titles,
|
||||
List<String> urls,
|
||||
@@ -320,11 +309,7 @@ public class SimilarAsinCozeClient {
|
||||
List<Map<String, Object>> items = new ArrayList<>(asins.size());
|
||||
for (int i = 0; i < asins.size(); i++) {
|
||||
Map<String, Object> item = new LinkedHashMap<>();
|
||||
item.put("group_key", groupKeys.get(i));
|
||||
item.put("row_token", rowTokens.get(i));
|
||||
item.put("row_id", rowIds.get(i));
|
||||
item.put("asin", asins.get(i));
|
||||
item.put("country", countries.get(i));
|
||||
item.put("sku", skus.get(i));
|
||||
item.put("title", titles.get(i));
|
||||
item.put("url", urls.get(i));
|
||||
|
||||
@@ -37,7 +37,10 @@ import java.util.List;
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/api/similar-asin")
|
||||
@Tag(name = "相似ASIN检测", description = "相似ASIN检测任务接口。前端上传 Excel 后由 Java 解析并创建任务;Python 回传商品数据;Java 负责攒批调用 Coze、补齐子行、生成最终 xlsx 并上传 OSS。")
|
||||
@Tag(
|
||||
name = "相似 ASIN 检测",
|
||||
description = "前端上传 Excel 后由 Java 解析并创建任务;Python 按分组抓取商品数据并回传;Java 再按批调用 Coze 并生成最终 xlsx。"
|
||||
)
|
||||
public class SimilarAsinController {
|
||||
|
||||
private final SimilarAsinTaskService service;
|
||||
@@ -69,13 +72,19 @@ public class SimilarAsinController {
|
||||
}
|
||||
|
||||
@PostMapping("/parse")
|
||||
@Operation(summary = "解析 Excel 并创建任务", description = "解析上传后的 Excel 文件,提取 id、ASIN、国家、URL、标题等字段。返回给前端的数据只包含整数 id 和 n_1 行;n_2、n_3 等子行会保存在 OSS 解析载荷中,用于最终结果补齐。创建后的任务状态为 PENDING,不会自动推送 Python。")
|
||||
@Operation(
|
||||
summary = "解析 Excel 并创建任务",
|
||||
description = "解析上传后的 Excel 文件,提取 id、ASIN、国家、URL、标题、SKU 等字段。返回给前端的数据同时包含全量有效行 items,以及按主数据块分组后的 groups,便于 Python 按组抓取后再回传。创建后的任务状态为 PENDING,不会自动推送 Python。"
|
||||
)
|
||||
public ApiResponse<SimilarAsinParseVo> parse(@Valid @RequestBody SimilarAsinParseRequest request) {
|
||||
return ApiResponse.success(service.parseAndCreateTask(request));
|
||||
}
|
||||
|
||||
@GetMapping("/tasks/{taskId}/parsed-payload")
|
||||
@Operation(summary = "获取货源查询完整解析载荷", description = "给 Python 队列消费端使用。parse 接口只返回轻量预览,大文件完整行数据通过该接口按 taskId 拉取。")
|
||||
@Operation(
|
||||
summary = "获取货源查询完整解析载荷",
|
||||
description = "给 Python 队列消费端使用。返回全量有效行 items/allItems 以及分组后的 groups,便于 Python 按组抓取后回传。"
|
||||
)
|
||||
public ApiResponse<SimilarAsinParsedPayloadDto> parsedPayload(
|
||||
@Parameter(description = "货源查询任务 ID", required = true, example = "7004")
|
||||
@PathVariable Long taskId,
|
||||
@@ -85,17 +94,17 @@ public class SimilarAsinController {
|
||||
}
|
||||
|
||||
@GetMapping("/dashboard")
|
||||
@Operation(summary = "查询相似ASIN检测总览", description = "查询当前用户的运行中、成功、失败和已结束任务数量。页面进入时请求一次即可,不需要持续轮询。")
|
||||
@Operation(summary = "查询相似 ASIN 检测总览", description = "查询当前用户的运行中、成功、失败和已结束任务数量。")
|
||||
public ApiResponse<SimilarAsinDashboardVo> dashboard(
|
||||
@Parameter(description = "当前用户 ID,用于隔离不同用户的任务和历史记录。", required = true, example = "1")
|
||||
@Parameter(description = "当前用户 ID", required = true, example = "1")
|
||||
@RequestParam("user_id") Long userId) {
|
||||
return ApiResponse.success(service.dashboard(userId));
|
||||
}
|
||||
|
||||
@GetMapping("/history")
|
||||
@Operation(summary = "查询相似ASIN检测历史", description = "查询当前用户最近的相似ASIN检测历史记录,包含源文件名、任务状态、行数、错误信息和最终 xlsx 下载地址。")
|
||||
@Operation(summary = "查询相似 ASIN 检测历史", description = "查询当前用户最近的相似 ASIN 检测历史记录。")
|
||||
public ApiResponse<SimilarAsinHistoryVo> history(
|
||||
@Parameter(description = "当前用户 ID,用于查询该用户自己的历史记录。", required = true, example = "1")
|
||||
@Parameter(description = "当前用户 ID", required = true, example = "1")
|
||||
@RequestParam("user_id") Long userId,
|
||||
@Parameter(description = "history limit, default 50, max 100", example = "50")
|
||||
@RequestParam(value = "limit", required = false, defaultValue = "50") Integer limit) {
|
||||
@@ -103,26 +112,29 @@ public class SimilarAsinController {
|
||||
}
|
||||
|
||||
@PostMapping("/tasks/progress/batch")
|
||||
@Operation(summary = "批量查询任务进度", description = "前端只对活跃任务调用该接口,建议 6 秒一次。接口只返回轻量任务状态,不返回明细结果。")
|
||||
@Operation(summary = "批量查询任务进度", description = "前端仅对活跃任务轮询该接口,返回轻量任务状态。")
|
||||
public ApiResponse<SimilarAsinTaskBatchVo> progress(@Valid @RequestBody SimilarAsinTaskBatchRequest request) {
|
||||
return ApiResponse.success(service.progressBatch(request.getTaskIds()));
|
||||
}
|
||||
|
||||
@PostMapping("/tasks/{taskId}/activate")
|
||||
@Operation(summary = "激活任务", description = "前端手动推送 Python 队列成功后调用,将任务从 PENDING 改为 RUNNING,并记录后端内部活跃时间。后续活跃时间由 Python 回传结果接口自动刷新,不需要单独心跳接口。")
|
||||
@Operation(summary = "激活任务", description = "前端手动推送 Python 队列成功后调用,将任务从 PENDING 改为 RUNNING。")
|
||||
public ApiResponse<Void> activate(
|
||||
@Parameter(description = "相似ASIN检测任务 ID,即解析接口返回的 taskId。", required = true, example = "3938")
|
||||
@Parameter(description = "相似 ASIN 检测任务 ID", required = true, example = "3938")
|
||||
@PathVariable Long taskId,
|
||||
@Parameter(description = "当前用户 ID,必须与创建任务的用户一致。", required = true, example = "1")
|
||||
@Parameter(description = "当前用户 ID", required = true, example = "1")
|
||||
@RequestParam("user_id") Long userId) {
|
||||
service.activateTask(taskId, userId);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@PostMapping("/tasks/{taskId}/result")
|
||||
@Operation(summary = "提交 Python 回传结果", description = "Python 回传商品数据接口。请通过 groups[].items[] 回传分组结果;Java 先原样保存回传数据,再内部攒批调用 Coze。done=true 表示 Python 已完成全部回传,Java 会强制处理剩余未满批的数据并生成最终 xlsx。")
|
||||
@Operation(
|
||||
summary = "提交 Python 回传结果",
|
||||
description = "Python 请通过 groups[].items[] 回传分组抓取结果;Java 先原样保存回传数据,再内部攒批调用 Coze。done=true 表示 Python 已完成全部回传,Java 会强制处理剩余未满批的数据并生成最终 xlsx。"
|
||||
)
|
||||
public ApiResponse<Void> result(
|
||||
@Parameter(description = "相似ASIN检测任务 ID。任务必须处于 RUNNING 状态。", required = true, example = "3938")
|
||||
@Parameter(description = "相似 ASIN 检测任务 ID,任务必须处于 RUNNING 状态", required = true, example = "3938")
|
||||
@PathVariable Long taskId,
|
||||
@Valid @RequestBody SimilarAsinSubmitResultRequest request,
|
||||
jakarta.servlet.http.HttpServletResponse response) {
|
||||
@@ -133,29 +145,29 @@ public class SimilarAsinController {
|
||||
}
|
||||
|
||||
@DeleteMapping("/tasks/{taskId}")
|
||||
@Operation(summary = "删除任务", description = "删除当前用户的一条相似ASIN检测任务,同时清理任务结果、scope 状态和分片记录。")
|
||||
@Operation(summary = "删除任务", description = "删除当前用户的一条相似 ASIN 检测任务,同时清理任务结果、scope 状态和分片记录。")
|
||||
public ApiResponse<Void> deleteTask(
|
||||
@Parameter(description = "相似ASIN检测任务 ID。", required = true, example = "3938")
|
||||
@Parameter(description = "相似 ASIN 检测任务 ID", required = true, example = "3938")
|
||||
@PathVariable Long taskId,
|
||||
@Parameter(description = "当前用户 ID,必须与创建任务的用户一致。", required = true, example = "1")
|
||||
@Parameter(description = "当前用户 ID", required = true, example = "1")
|
||||
@RequestParam("user_id") Long userId) {
|
||||
service.deleteTask(taskId, userId);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@DeleteMapping("/history/{resultId}")
|
||||
@Operation(summary = "删除历史记录", description = "删除当前用户的一条相似ASIN检测历史记录。只删除 biz_file_result 记录,不主动删除任务主记录。")
|
||||
@Operation(summary = "删除历史记录", description = "删除当前用户的一条相似 ASIN 检测历史记录。")
|
||||
public ApiResponse<Void> deleteHistory(
|
||||
@Parameter(description = "历史结果记录 ID,即 history 接口返回的 resultId。", required = true, example = "1001")
|
||||
@Parameter(description = "历史结果记录 ID", required = true, example = "1001")
|
||||
@PathVariable Long resultId,
|
||||
@Parameter(description = "当前用户 ID。", required = true, example = "1")
|
||||
@Parameter(description = "当前用户 ID", required = true, example = "1")
|
||||
@RequestParam("user_id") Long userId) {
|
||||
service.deleteHistory(resultId, userId);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@GetMapping("/results/{resultId}/download")
|
||||
@Operation(summary = "下载相似ASIN检测结果文件")
|
||||
@Operation(summary = "下载相似 ASIN 检测结果文件")
|
||||
public void downloadResult(
|
||||
@Parameter(description = "结果记录 ID", required = true, example = "1001")
|
||||
@PathVariable Long resultId,
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "相似ASIN检测解析载荷")
|
||||
@Schema(description = "相似 ASIN 解析载荷")
|
||||
public class SimilarAsinParsedPayloadDto {
|
||||
@Schema(description = "AI 提示词")
|
||||
private String aiPrompt;
|
||||
@@ -23,12 +23,12 @@ public class SimilarAsinParsedPayloadDto {
|
||||
@Schema(description = "Excel 原始表头列表")
|
||||
private List<String> headers = new ArrayList<>();
|
||||
|
||||
@Schema(description = "兼容旧链路的平铺有效行,现为全部有效行")
|
||||
@Schema(description = "平铺的全量有效行")
|
||||
private List<SimilarAsinParsedRowVo> items = new ArrayList<>();
|
||||
|
||||
@Schema(description = "按相邻主 ID 块分组后的完整数据")
|
||||
@Schema(description = "按主数据块分组后的完整数据,每个 group.items 都是需要 Python 抓取的全量子行")
|
||||
private List<SimilarAsinParsedGroupVo> groups = new ArrayList<>();
|
||||
|
||||
@Schema(description = "完整有效行")
|
||||
@Schema(description = "全量有效行,用于兼容旧链路与结果文件组装")
|
||||
private List<SimilarAsinParsedRowVo> allItems = new ArrayList<>();
|
||||
}
|
||||
|
||||
@@ -2564,7 +2564,6 @@ public class SimilarAsinTaskService {
|
||||
vo.setValues(readRowValues(row, headers, formatter));
|
||||
allRows.add(vo);
|
||||
}
|
||||
hydratePromptFields(allRows);
|
||||
if (allRows.isEmpty()) {
|
||||
throw new BusinessException("no valid similar ASIN rows");
|
||||
}
|
||||
@@ -2577,29 +2576,6 @@ public class SimilarAsinTaskService {
|
||||
}
|
||||
}
|
||||
|
||||
private void hydratePromptFields(List<SimilarAsinParsedRowVo> rows) {
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, List<SimilarAsinParsedRowVo>> rowsByBaseId = groupRowsByBaseId(rows);
|
||||
for (List<SimilarAsinParsedRowVo> siblings : rowsByBaseId.values()) {
|
||||
String title = "";
|
||||
String url = "";
|
||||
for (SimilarAsinParsedRowVo sibling : siblings) {
|
||||
title = firstNonBlank(title, sibling.getTitle());
|
||||
url = firstNonBlank(url, sibling.getUrl());
|
||||
}
|
||||
for (SimilarAsinParsedRowVo sibling : siblings) {
|
||||
if (normalize(sibling.getTitle()).isBlank()) {
|
||||
sibling.setTitle(title);
|
||||
}
|
||||
if (normalize(sibling.getUrl()).isBlank()) {
|
||||
sibling.setUrl(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasPromptFields(SimilarAsinParsedRowVo row) {
|
||||
return row != null && (!normalize(row.getTitle()).isBlank() || !normalize(row.getUrl()).isBlank());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user