提交更改

This commit is contained in:
super
2026-06-05 21:25:03 +08:00
parent 95ccb00d25
commit 8ab647c419
39 changed files with 1390 additions and 92 deletions
@@ -587,9 +587,11 @@ public class AppearancePatentCozeClient {
}
if (isNoTitleRisk(result)) {
row.setTitleRisk(NO_INFRINGEMENT);
} else {
} else if (shouldCheckBrand(result)) {
BrandCheckClient.BrandCheckBatchResult brandCheck = brandCheckClient.checkTitleText(result.title(), "Terms");
row.setTitleRisk(buildTitleRisk(result.title(), brandCheck));
} else {
row.setTitleRisk("");
}
row.setAppearanceRisk(result.appearance());
row.setPatentRisk(result.patent());
@@ -603,8 +605,13 @@ public class AppearancePatentCozeClient {
private boolean isNoTitleRisk(CozeResult result) {
return result != null
&& "".equals(normalize(result.title()))
&& "".equals(normalize(result.titleReason()));
&& "".equals(normalize(result.title()));
}
private boolean shouldCheckBrand(CozeResult result) {
return result != null
&& !normalize(result.title()).isBlank()
&& !normalize(result.appearance()).isBlank();
}
String buildTitleRisk(String cozeTitle, BrandCheckClient.BrandCheckBatchResult brandCheck) {
@@ -83,7 +83,7 @@ public class AppearancePatentResultRowDto {
private String patentReason;
@JsonAlias({"score", "Score", "评分"})
@Schema(description = "Coze 回流的评分(外观维度),透传到最终 xlsx 第一张 sheet 的“评分”列", accessMode = Schema.AccessMode.READ_ONLY)
@Schema(description = "Coze 回流的评分(外观维度),仅保留回流值,不写入最终 xlsx", accessMode = Schema.AccessMode.READ_ONLY)
private String score;
/**
@@ -3,6 +3,9 @@ package com.nanri.aiimage.modules.appearancepatent.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.LinkedHashMap;
import java.util.Map;
@Data
@Schema(description = "外观专利检测解析出的单行数据")
public class AppearancePatentParsedRowVo {
@@ -44,4 +47,7 @@ public class AppearancePatentParsedRowVo {
@Schema(description = "商品标题。", example = "Women Floral Dress Summer Casual")
private String title;
@Schema(description = "该 Excel 行的原始列值映射。最终生成 xlsx 时可从这里读取卖家名称、品牌等扩展字段。", example = "{\"id\":\"2_1\",\"asin\":\"B0CJ8SNXXV\",\"国家\":\"英国\"}")
private Map<String, String> values = new LinkedHashMap<>();
}
@@ -125,12 +125,13 @@ public class AppearancePatentTaskService {
"id",
"asin",
"国家",
"卖家名称",
"品牌",
"价格",
"标题维度(商标)",
"外观维度(外观设计专利)",
"结论",
"状态",
"评分"
"状态"
);
private final LocalFileStorageService localFileStorageService;
@@ -420,13 +421,7 @@ public class AppearancePatentTaskService {
}
public void submitResult(Long taskId, AppearancePatentSubmitResultRequest request) {
TaskDistributedLockService.LockHandle lockHandle = acquireTaskLock(taskId, TASK_LOCK_WAIT_MILLIS);
if (lockHandle == null) {
throw new BusinessException(40902, "任务正在处理上一批结果,请稍后重试");
}
try (lockHandle) {
submitResultLocked(taskId, request);
}
submitResultLocked(taskId, request);
}
private void submitResultLocked(Long taskId, AppearancePatentSubmitResultRequest request) {
@@ -1328,7 +1323,8 @@ public class AppearancePatentTaskService {
}
}
}
boolean failed = finalError != null && !finalError.isBlank();
boolean failedByConclusion = hasInfringingPersistedConclusion(task.getId());
boolean failed = finalError != null && !finalError.isBlank() || failedByConclusion;
boolean waitingForAssemble = !failed && assembleWorkbook && !shouldAssembleSynchronously();
task.setStatus(waitingForAssemble ? STATUS_RUNNING : (failed ? STATUS_FAILED : STATUS_SUCCESS));
task.setSuccessFileCount(failed ? 0 : 1);
@@ -2407,7 +2403,8 @@ public class AppearancePatentTaskService {
saveFileBuildProgress(task, job, totalProgressUnits, totalProgressUnits - 1, "正在上传结果文件");
fileResultMapper.updateById(result);
boolean taskAlreadyFailed = STATUS_FAILED.equals(task.getStatus())
|| (task.getErrorMessage() != null && !task.getErrorMessage().isBlank());
|| (task.getErrorMessage() != null && !task.getErrorMessage().isBlank())
|| hasInfringingPersistedConclusion(task.getId());
String existingError = task.getErrorMessage();
task.setStatus(taskAlreadyFailed ? STATUS_FAILED : STATUS_SUCCESS);
task.setSuccessFileCount(taskAlreadyFailed ? 0 : 1);
@@ -3300,9 +3297,9 @@ public class AppearancePatentTaskService {
headerStyle.setFont(font);
List<String> resultHeaders = new ArrayList<>(RESULT_HEADERS);
resultHeaders.add(4, "标题");
resultHeaders.add(5, "图片链接");
resultHeaders.add(6, "sku");
resultHeaders.add(6, "标题");
resultHeaders.add(7, "图片链接");
resultHeaders.add(8, "sku");
Row header = sheet.createRow(0);
for (int i = 0; i < resultHeaders.size(); i++) {
Cell cell = header.createCell(i);
@@ -3314,24 +3311,21 @@ public class AppearancePatentTaskService {
List<AppearancePatentParsedRowVo> rowsToWrite = receivedRows == null ? List.of() : receivedRows;
for (AppearancePatentParsedRowVo parsedRow : rowsToWrite) {
AppearancePatentResultRowDto resultRow = findResultRow(parsedRow, resultMap);
String missingReason = "";
if (resultRow == null) {
missingReason = hasPromptFields(parsedRow) ? "未匹配到检测结果" : "未送检:缺少标题或图片";
}
Row row = sheet.createRow(rowIndex++);
int col = 0;
row.createCell(col++).setCellValue(firstNonBlank(parsedRow.getDisplayId(), parsedRow.getSourceId()));
row.createCell(col++).setCellValue(firstNonBlank(parsedRow.getAsin(), ""));
row.createCell(col++).setCellValue(firstNonBlank(parsedRow.getCountry(), ""));
row.createCell(col++).setCellValue(readValueByHeader(parsedRow, "卖家名称", "卖家名", "卖家", "店铺名称", "店铺名", "seller name", "seller_name", "seller-name", "sellername", "store name", "shop name"));
row.createCell(col++).setCellValue(readValueByHeader(parsedRow, "品牌", "brand"));
row.createCell(col++).setCellValue(firstNonBlank(parsedRow.getPrice(), ""));
row.createCell(col++).setCellValue(resultRow == null ? firstNonBlank(parsedRow.getTitle(), "") : firstNonBlank(resultRow.getTitle(), parsedRow.getTitle()));
row.createCell(col++).setCellValue(resultRow == null ? firstNonBlank(parsedRow.getUrl(), "") : firstNonBlank(resultRow.getUrl(), parsedRow.getUrl()));
row.createCell(col++).setCellValue(resultRow == null ? firstNonBlank(parsedRow.getSku(), "") : firstNonBlank(resultRow.getSku(), parsedRow.getSku()));
row.createCell(col++).setCellValue(resultRow == null ? missingReason : userFacingCozeCellValue(resultRow, resultRow.getTitleRisk()));
row.createCell(col++).setCellValue(resultRow == null ? missingReason : userFacingCozeCellValue(resultRow, resultRow.getAppearanceRisk()));
row.createCell(col++).setCellValue(resultRow == null ? "未送检" : userFacingConclusion(resultRow));
row.createCell(col++).setCellValue(resultRow == null ? "" : userFacingStatus(resultRow));
row.createCell(col).setCellValue(resultRow == null ? "" : firstNonBlank(resultRow.getScore(), ""));
row.createCell(col++).setCellValue(resultRow == null ? "" : userFacingCozeCellValue(resultRow, resultRow.getTitleRisk()));
row.createCell(col++).setCellValue(resultRow == null ? "" : userFacingCozeCellValue(resultRow, resultRow.getAppearanceRisk()));
row.createCell(col++).setCellValue(resultRow == null ? "" : userFacingConclusion(resultRow));
row.createCell(col).setCellValue(resultRow == null ? "" : userFacingStatus(resultRow));
}
writeReasonSheet(workbook, headerStyle, rowsToWrite, resultMap);
workbook.write(fos);
@@ -3519,6 +3513,7 @@ public class AppearancePatentTaskService {
vo.setSku(skuCol >= 0 ? cell(row, skuCol, formatter) : "");
vo.setUrl(urlCol >= 0 ? cell(row, urlCol, formatter) : "");
vo.setTitle(titleCol >= 0 ? cell(row, titleCol, formatter) : "");
vo.setValues(readRowValues(row, headers, formatter));
parsedRows.add(new ParsedAppearanceRow(vo, statusCol >= 0 ? cell(row, statusCol, formatter) : ""));
}
List<AppearancePatentParsedRowVo> allRows = parsedRows.stream()
@@ -3590,6 +3585,14 @@ public class AppearancePatentTaskService {
return headers;
}
private Map<String, String> readRowValues(Row row, List<String> headers, DataFormatter formatter) {
Map<String, String> values = new LinkedHashMap<>();
for (int i = 0; i < headers.size(); i++) {
values.put(headers.get(i), cell(row, i, formatter));
}
return values;
}
private boolean isAppearanceResultWorkbook(List<String> headers) {
return hasHeader(headers, "状态")
&& (hasHeader(headers, "结论")
@@ -3790,7 +3793,8 @@ public class AppearancePatentTaskService {
vo.setDownloadUrl(null);
attachFileJobState(vo, row, job);
vo.setTaskStatus(task == null ? null : task.getStatus());
vo.setSuccess(row.getSuccess() != null && row.getSuccess() == 1);
vo.setSuccess(row.getResultFileUrl() != null && !row.getResultFileUrl().isBlank()
|| row.getSuccess() != null && row.getSuccess() == 1);
vo.setError(row.getErrorMessage());
vo.setRowCount(row.getRowCount());
vo.setCreatedAt(fmt(row.getCreatedAt()));
@@ -4143,6 +4147,51 @@ public class AppearancePatentTaskService {
return normalize(id) + "::" + normalize(asin).toUpperCase(Locale.ROOT) + "::" + normalize(country);
}
private String readValueByHeader(AppearancePatentParsedRowVo row, String... candidates) {
if (row == null || row.getValues() == null || row.getValues().isEmpty() || candidates == null) {
return "";
}
for (Map.Entry<String, String> entry : row.getValues().entrySet()) {
String header = normalize(entry.getKey()).toLowerCase(Locale.ROOT);
for (String candidate : candidates) {
String expected = normalize(candidate).toLowerCase(Locale.ROOT);
if (!expected.isBlank() && header.contains(expected)) {
return entry.getValue() == null ? "" : entry.getValue();
}
}
}
return "";
}
private boolean hasInfringingPersistedConclusion(Long taskId) {
if (taskId == null) {
return false;
}
return loadPersistedResultRows(taskId).values().stream()
.map(AppearancePatentResultRowDto::getConclusion)
.anyMatch(this::isInfringingConclusion);
}
private boolean isInfringingConclusion(String value) {
String normalized = normalize(value);
return normalized.contains("侵权")
&& !isNoInfringementConclusion(normalized);
}
private boolean isNoInfringementConclusion(String value) {
String normalized = normalize(value);
if (normalized.isBlank()) {
return false;
}
return normalized.contains("无侵权")
|| normalized.contains("未侵权")
|| normalized.contains("不侵权")
|| normalized.contains("未发现") && normalized.contains("侵权")
|| normalized.contains("未见") && normalized.contains("侵权")
|| normalized.contains("不存在") && normalized.contains("侵权")
|| normalized.contains("无明显") && normalized.contains("侵权");
}
private String userFacingCozeCellValue(AppearancePatentResultRowDto row, String value) {
String normalizedValue = normalize(value);
if (!normalizedValue.isBlank() && !isTechnicalCozeFailure(normalizedValue)) {
@@ -4180,11 +4229,13 @@ public class AppearancePatentTaskService {
if (row == null) {
return "";
}
String original = firstNonBlank(row.getStatus(), "");
if (!row.isFailureSyntheticStatus()) {
return original;
}
return firstNonBlank(row.getError(), "");
String conclusion = userFacingConclusion(row);
return resolveResultStatus(conclusion);
}
static String resolveResultStatus(String conclusion) {
String normalized = conclusion == null ? "" : conclusion.trim();
return normalized.isBlank() ? "\u5931\u8d25" : "\u6210\u529f";
}
private boolean isTechnicalCozeFailure(String value) {