+149
-53
@@ -2678,9 +2678,10 @@ public class AppearancePatentTaskService {
|
||||
markCozeStateTerminal(state, COZE_STATUS_FAILED, "Coze pending submit task missing");
|
||||
return;
|
||||
}
|
||||
Map<String, AppearancePatentResultRowDto> currentRows = loadPersistedResultRows(task.getId());
|
||||
Map<String, AppearancePatentResultRowDto> currentRows = loadPersistedResultRowsForResultRows(
|
||||
task.getId(), batchRows);
|
||||
List<AppearancePatentResultRowDto> currentBatchRows = batchRows.stream()
|
||||
.map(row -> currentRows.get(rowKey(row)))
|
||||
.map(row -> findPersistedResultRow(row, currentRows))
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
if (currentBatchRows.size() == batchRows.size()
|
||||
@@ -3006,45 +3007,48 @@ public class AppearancePatentTaskService {
|
||||
|
||||
private void assembleResultWorkbook(FileTaskEntity task, FileResultEntity result) {
|
||||
AppearancePatentParsedPayloadDto parsed = readParsedPayload(task);
|
||||
Map<String, AppearancePatentResultRowDto> resultMap = loadPersistedResultRowsWithRetry(task.getId(), parsed.getAllItems().size());
|
||||
List<AppearancePatentParsedRowVo> receivedRows = filterReceivedParsedRows(parsed.getAllItems(), resultMap);
|
||||
long resolvedRows = receivedRows.stream()
|
||||
.filter(row -> findResultRow(row, resultMap) != null)
|
||||
.count();
|
||||
long reasonRows = resultMap.values().stream()
|
||||
.filter(this::hasReasonFields)
|
||||
.count();
|
||||
log.info("[appearance-patent] assemble workbook taskId={} parsedRows={} receivedRows={} resultRows={} resolvedRows={} reasonRows={}",
|
||||
task.getId(), parsed.getAllItems().size(), receivedRows.size(), resultMap.size(), resolvedRows, reasonRows);
|
||||
if (!parsed.getAllItems().isEmpty() && receivedRows.isEmpty()) {
|
||||
List<AppearancePatentParsedRowVo> parsedRows = parsed.getAllItems() == null
|
||||
? List.of() : parsed.getAllItems();
|
||||
List<SourceRows> sourceRows = splitRowsBySourceFile(parsed, parsedRows, result.getSourceFilename());
|
||||
if (sourceRows.isEmpty()) {
|
||||
throw new BusinessException("外观专利检测结果为空,请稍后重试生成结果文件");
|
||||
}
|
||||
validateCompleteCozeCoverage(task.getId(), receivedRows, resultMap);
|
||||
// 公共分组处理:同 baseId 连续行视为一组(如 1、1_1、1_2),组内任一行结论命中
|
||||
// "已侵权"/"侵权"(包含匹配,"已侵权"优先),则组内所有行结论统一为该标准值。
|
||||
// 单行组跳过。仅修改 conclusion 列,不影响其他列。
|
||||
int conclusionPropagated = CozeGroupResultPropagator.propagateByGroup(
|
||||
receivedRows,
|
||||
AppearancePatentParsedRowVo::getDisplayId,
|
||||
row -> findResultRow(row, resultMap),
|
||||
AppearancePatentResultRowDto::getConclusion,
|
||||
AppearancePatentResultRowDto::setConclusion,
|
||||
List.of("已侵权", "侵权"),
|
||||
"[appearance-patent] taskId=" + task.getId()
|
||||
);
|
||||
if (conclusionPropagated > 0) {
|
||||
log.info("[appearance-patent] group propagate conclusion taskId={} updatedRows={}",
|
||||
task.getId(), conclusionPropagated);
|
||||
}
|
||||
int receivedRowCount = 0;
|
||||
long resolvedRows = 0L;
|
||||
long reasonRows = 0L;
|
||||
int conclusionPropagated = 0;
|
||||
File outputDir = new File(storageProperties.getLocalTempDir(), "appearance-patent-result");
|
||||
if (!outputDir.exists() && !outputDir.mkdirs()) {
|
||||
throw new BusinessException("创建结果目录失败");
|
||||
}
|
||||
List<SourceRows> sourceRows = splitRowsBySourceFile(parsed, receivedRows, result.getSourceFilename());
|
||||
List<SourceResultWorkbook> workbooks = new ArrayList<>();
|
||||
File zip = null;
|
||||
try {
|
||||
for (SourceRows item : sourceRows) {
|
||||
Map<String, AppearancePatentResultRowDto> resultMap =
|
||||
loadPersistedResultRowsForRowsWithRetry(task.getId(), item.rows());
|
||||
List<AppearancePatentParsedRowVo> receivedRows = filterReceivedParsedRows(item.rows(), resultMap);
|
||||
if (receivedRows.isEmpty()) {
|
||||
resultMap.clear();
|
||||
continue;
|
||||
}
|
||||
receivedRowCount += receivedRows.size();
|
||||
resolvedRows += receivedRows.stream()
|
||||
.filter(row -> findResultRow(row, resultMap) != null)
|
||||
.count();
|
||||
reasonRows += resultMap.values().stream()
|
||||
.filter(this::hasReasonFields)
|
||||
.count();
|
||||
validateCompleteCozeCoverage(task.getId(), receivedRows, resultMap);
|
||||
conclusionPropagated += CozeGroupResultPropagator.propagateByGroup(
|
||||
receivedRows,
|
||||
AppearancePatentParsedRowVo::getDisplayId,
|
||||
row -> findResultRow(row, resultMap),
|
||||
AppearancePatentResultRowDto::getConclusion,
|
||||
AppearancePatentResultRowDto::setConclusion,
|
||||
List.of("已侵权", "侵权"),
|
||||
"[appearance-patent] taskId=" + task.getId()
|
||||
);
|
||||
String filename = safeFileStem(item.sourceFilename()) + "-result.xlsx";
|
||||
String tempFilename = safeFileStem(item.sourceFilename())
|
||||
+ "-" + task.getId()
|
||||
@@ -3052,12 +3056,20 @@ public class AppearancePatentTaskService {
|
||||
+ "-" + UUID.randomUUID()
|
||||
+ "-result.xlsx";
|
||||
File xlsx = new File(outputDir, tempFilename);
|
||||
writeResultWorkbook(xlsx, parsed, item.rows(), resultMap);
|
||||
workbooks.add(new SourceResultWorkbook(xlsx, filename, item.rows().size()));
|
||||
writeResultWorkbook(xlsx, parsed, receivedRows, resultMap);
|
||||
workbooks.add(new SourceResultWorkbook(xlsx, filename, receivedRows.size()));
|
||||
resultMap.clear();
|
||||
}
|
||||
if (workbooks.isEmpty()) {
|
||||
throw new BusinessException("外观专利检测结果为空,请稍后重试生成结果文件");
|
||||
}
|
||||
log.info("[appearance-patent] assemble workbook taskId={} parsedRows={} receivedRows={} resultRows={} resolvedRows={} reasonRows={}",
|
||||
task.getId(), parsedRows.size(), receivedRowCount,
|
||||
workbooks.stream().mapToInt(SourceResultWorkbook::rowCount).sum(), resolvedRows, reasonRows);
|
||||
if (conclusionPropagated > 0) {
|
||||
log.info("[appearance-patent] group propagate conclusion taskId={} updatedRows={}",
|
||||
task.getId(), conclusionPropagated);
|
||||
}
|
||||
|
||||
File uploadFile;
|
||||
String filename;
|
||||
@@ -3085,7 +3097,7 @@ public class AppearancePatentTaskService {
|
||||
result.setResultFileUrl(objectKey);
|
||||
result.setResultFileSize(uploadFile.length());
|
||||
result.setResultContentType(contentType);
|
||||
result.setRowCount(receivedRows.size());
|
||||
result.setRowCount(receivedRowCount);
|
||||
} finally {
|
||||
for (SourceResultWorkbook workbook : workbooks) {
|
||||
if (workbook.file().exists() && !workbook.file().delete()) {
|
||||
@@ -3249,18 +3261,6 @@ public class AppearancePatentTaskService {
|
||||
&& hasUsableCozeField(row.getConclusion());
|
||||
}
|
||||
|
||||
private Map<String, AppearancePatentResultRowDto> loadPersistedResultRows(Long taskId) {
|
||||
Map<String, AppearancePatentResultRowDto> result = new LinkedHashMap<>();
|
||||
List<TaskChunkEntity> chunks = taskChunkMapper.selectList(new LambdaQueryWrapper<TaskChunkEntity>()
|
||||
.eq(TaskChunkEntity::getTaskId, taskId)
|
||||
.eq(TaskChunkEntity::getModuleType, MODULE_TYPE)
|
||||
.orderByAsc(TaskChunkEntity::getChunkIndex));
|
||||
for (TaskChunkEntity chunk : chunks) {
|
||||
result.putAll(readChunkRows(chunk));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean hasPersistedResultRows(Long taskId) {
|
||||
if (taskId == null) {
|
||||
return false;
|
||||
@@ -3271,25 +3271,121 @@ public class AppearancePatentTaskService {
|
||||
return count != null && count > 0;
|
||||
}
|
||||
|
||||
private Map<String, AppearancePatentResultRowDto> loadPersistedResultRowsWithRetry(Long taskId, int expectedParsedRows) {
|
||||
Map<String, AppearancePatentResultRowDto> result = loadPersistedResultRows(taskId);
|
||||
if (expectedParsedRows <= 0 || !result.isEmpty()) {
|
||||
private Map<String, AppearancePatentResultRowDto> loadPersistedResultRowsForRowsWithRetry(
|
||||
Long taskId, List<AppearancePatentParsedRowVo> requestedRows) {
|
||||
Set<String> requestedKeys = resultLookupKeysForParsedRows(requestedRows);
|
||||
Map<String, AppearancePatentResultRowDto> result =
|
||||
loadPersistedResultRowsForKeys(taskId, requestedKeys);
|
||||
if (requestedKeys.isEmpty() || !result.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
for (int attempt = 1; attempt <= RESULT_ROWS_READ_RETRY_LIMIT && result.isEmpty(); attempt++) {
|
||||
sleepBeforeResultRowsRetry(attempt);
|
||||
result = loadPersistedResultRows(taskId);
|
||||
result = loadPersistedResultRowsForKeys(taskId, requestedKeys);
|
||||
if (!result.isEmpty()) {
|
||||
log.info("[appearance-patent] result rows recovered after retry taskId={} attempt={} rows={}",
|
||||
taskId, attempt, result.size());
|
||||
return result;
|
||||
}
|
||||
log.warn("[appearance-patent] result rows still empty after retry taskId={} attempt={}/{}",
|
||||
taskId, attempt, RESULT_ROWS_READ_RETRY_LIMIT);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, AppearancePatentResultRowDto> loadPersistedResultRowsForResultRows(
|
||||
Long taskId, List<AppearancePatentResultRowDto> requestedRows) {
|
||||
Set<String> requestedKeys = resultLookupKeysForResultRows(requestedRows);
|
||||
return loadPersistedResultRowsForKeys(taskId, requestedKeys);
|
||||
}
|
||||
|
||||
private Map<String, AppearancePatentResultRowDto> loadPersistedResultRowsForKeys(
|
||||
Long taskId, Set<String> requestedKeys) {
|
||||
Map<String, AppearancePatentResultRowDto> result = new LinkedHashMap<>();
|
||||
if (taskId == null || requestedKeys == null || requestedKeys.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
Long lastChunkId = null;
|
||||
while (true) {
|
||||
LambdaQueryWrapper<TaskChunkEntity> query = new LambdaQueryWrapper<TaskChunkEntity>()
|
||||
.eq(TaskChunkEntity::getTaskId, taskId)
|
||||
.eq(TaskChunkEntity::getModuleType, MODULE_TYPE)
|
||||
.orderByAsc(TaskChunkEntity::getId)
|
||||
.last("LIMIT 1");
|
||||
if (lastChunkId != null) {
|
||||
query.gt(TaskChunkEntity::getId, lastChunkId);
|
||||
}
|
||||
List<TaskChunkEntity> page = taskChunkMapper.selectList(query);
|
||||
if (page == null || page.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
TaskChunkEntity chunk = page.getFirst();
|
||||
appendRequestedResultRows(result, readChunkRows(chunk).values(), requestedKeys);
|
||||
lastChunkId = chunk.getId();
|
||||
if (lastChunkId == null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Set<String> resultLookupKeysForParsedRows(List<AppearancePatentParsedRowVo> rows) {
|
||||
Set<String> keys = new LinkedHashSet<>();
|
||||
for (AppearancePatentParsedRowVo row : rows == null ? List.<AppearancePatentParsedRowVo>of() : rows) {
|
||||
String primary = rowKey(row);
|
||||
if (!primary.isBlank()) {
|
||||
keys.add(primary);
|
||||
}
|
||||
String legacy = legacyRowKey(row);
|
||||
if (!legacy.isBlank()) {
|
||||
keys.add(legacy);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
private Set<String> resultLookupKeysForResultRows(List<AppearancePatentResultRowDto> rows) {
|
||||
Set<String> keys = new LinkedHashSet<>();
|
||||
for (AppearancePatentResultRowDto row : rows == null ? List.<AppearancePatentResultRowDto>of() : rows) {
|
||||
String primary = rowKey(row);
|
||||
if (!primary.isBlank()) {
|
||||
keys.add(primary);
|
||||
}
|
||||
String legacy = legacyRowKey(row);
|
||||
if (!legacy.isBlank()) {
|
||||
keys.add(legacy);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
private void appendRequestedResultRows(Map<String, AppearancePatentResultRowDto> sink,
|
||||
Iterable<AppearancePatentResultRowDto> rows,
|
||||
Set<String> requestedKeys) {
|
||||
if (rows == null || requestedKeys == null || requestedKeys.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (AppearancePatentResultRowDto row : rows) {
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
String primary = rowKey(row);
|
||||
String legacy = legacyRowKey(row);
|
||||
String matchedKey = requestedKeys.contains(primary)
|
||||
? primary : requestedKeys.contains(legacy) ? legacy : "";
|
||||
if (!matchedKey.isBlank()) {
|
||||
sink.put(matchedKey, row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AppearancePatentResultRowDto findPersistedResultRow(
|
||||
AppearancePatentResultRowDto row,
|
||||
Map<String, AppearancePatentResultRowDto> resultMap) {
|
||||
if (row == null || resultMap == null || resultMap.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
AppearancePatentResultRowDto result = resultMap.get(rowKey(row));
|
||||
return result == null ? resultMap.get(legacyRowKey(row)) : result;
|
||||
}
|
||||
|
||||
private void sleepBeforeResultRowsRetry(int attempt) {
|
||||
try {
|
||||
Thread.sleep(RESULT_ROWS_READ_RETRY_DELAY_MS * attempt);
|
||||
|
||||
Reference in New Issue
Block a user