提交更新

This commit is contained in:
super
2026-05-02 09:28:26 +08:00
parent aece8123c3
commit 3ce66a59ee
13 changed files with 380 additions and 33 deletions
@@ -149,9 +149,16 @@ public class AppearancePatentCozeClient {
}
String status = normalize(resolveWorkflowStatus(pollRoot)).toUpperCase(Locale.ROOT);
if (status.contains("FAIL") || status.contains("ERROR") || status.contains("CANCEL")) {
if (isFailedWorkflowStatus(status)) {
throw new IllegalStateException(firstNonBlank(resolveFailureMessage(pollRoot), "Coze async workflow failed"));
}
if (isSuccessfulWorkflowStatus(status)) {
String outputText = extractWorkflowOutputText(pollRoot);
if (!outputText.isBlank()) {
return wrapDataPayload(outputText);
}
throw new IllegalStateException("Coze async workflow completed without output");
}
sleepQuietly(Math.max(200, properties.getCozePollIntervalMillis()));
}
@@ -251,7 +258,7 @@ public class AppearancePatentCozeClient {
return List.of();
}
JsonNode dataRoot = objectMapper.readTree(dataText);
JsonNode array = dataRoot.path("data");
JsonNode array = dataRoot.isArray() ? dataRoot : dataRoot.path("data");
List<CozeResult> results = new ArrayList<>();
if (array.isArray()) {
for (JsonNode node : array) {
@@ -502,6 +509,23 @@ public class AppearancePatentCozeClient {
return discoverEmbeddedData(root);
}
private String extractWorkflowOutputText(JsonNode root) {
JsonNode outputNode = findFirstField(root, "output");
if (outputNode == null || outputNode.isNull() || outputNode.isMissingNode()) {
return "";
}
if (!outputNode.isTextual()) {
return outputNode.toString();
}
String output = normalize(outputNode.asText(""));
if (output.isBlank()) {
return "";
}
JsonNode parsedOutput = parseJsonOrMissing(output);
String nestedOutput = text(firstNonNull(parsedOutput.get("Output"), parsedOutput.get("output")));
return nestedOutput == null || nestedOutput.isBlank() ? output : nestedOutput;
}
private String discoverEmbeddedData(JsonNode node) {
if (node == null || node.isNull() || node.isMissingNode()) {
return "";
@@ -577,6 +601,22 @@ public class AppearancePatentCozeClient {
|| item.has("patent reason"));
}
private boolean isSuccessfulWorkflowStatus(String status) {
String normalized = normalize(status).toUpperCase(Locale.ROOT);
return normalized.equals("SUCCESS")
|| normalized.equals("SUCCEEDED")
|| normalized.equals("COMPLETED")
|| normalized.equals("COMPLETE")
|| normalized.equals("DONE");
}
private boolean isFailedWorkflowStatus(String status) {
String normalized = normalize(status).toUpperCase(Locale.ROOT);
return normalized.contains("FAIL")
|| normalized.contains("ERROR")
|| normalized.contains("CANCEL");
}
private JsonNode parseJsonOrMissing(String value) {
String normalized = normalize(value);
if (!(normalized.startsWith("{") || normalized.startsWith("["))) {
@@ -611,6 +651,38 @@ public class AppearancePatentCozeClient {
return findTextByFieldName(root, "execute_id", "executeId");
}
private JsonNode findFirstField(JsonNode node, String name) {
if (node == null || node.isMissingNode() || node.isNull() || name == null || name.isBlank()) {
return objectMapper.missingNode();
}
if (node.isTextual()) {
return findFirstField(parseJsonOrMissing(node.asText("")), name);
}
if (node.isArray()) {
for (JsonNode child : node) {
JsonNode found = findFirstField(child, name);
if (found != null && !found.isMissingNode() && !found.isNull()) {
return found;
}
}
return objectMapper.missingNode();
}
if (node.isObject()) {
JsonNode direct = node.get(name);
if (direct != null && !direct.isMissingNode() && !direct.isNull()) {
return direct;
}
for (java.util.Iterator<Map.Entry<String, JsonNode>> it = node.fields(); it.hasNext(); ) {
Map.Entry<String, JsonNode> entry = it.next();
JsonNode found = findFirstField(entry.getValue(), name);
if (found != null && !found.isMissingNode() && !found.isNull()) {
return found;
}
}
}
return objectMapper.missingNode();
}
private String findTextByFieldName(JsonNode node, String... names) {
if (node == null || node.isMissingNode() || node.isNull()) {
return "";
@@ -113,8 +113,8 @@ public class AppearancePatentTaskService {
private final TransientPayloadStorageService transientPayloadStorageService;
private final PlatformTransactionManager transactionManager;
@Transactional
public AppearancePatentParseVo parseAndCreateTask(AppearancePatentParseRequest request) {
long startedAt = System.nanoTime();
if (request.getUserId() == null || request.getUserId() <= 0) {
throw new BusinessException("user_id 不合法");
}
@@ -131,6 +131,7 @@ public class AppearancePatentTaskService {
List<String> mergedHeaders = new ArrayList<>();
int totalRows = 0;
int droppedRows = 0;
long parseStartedAt = System.nanoTime();
for (AppearancePatentSourceFileDto source : sourceFiles) {
if (source.getFileKey() == null || source.getFileKey().isBlank()) {
throw new BusinessException("fileKey 不能为空");
@@ -149,7 +150,9 @@ public class AppearancePatentTaskService {
if (allRows.isEmpty()) {
throw new BusinessException("未解析到有效 ASIN 数据");
}
long parsedAt = System.nanoTime();
List<AppearancePatentParsedGroupVo> groups = buildParsedGroups(allRows);
long groupedAt = System.nanoTime();
FileTaskEntity task = new FileTaskEntity();
task.setTaskNo(MODULE_TYPE + "-" + IdUtil.getSnowflakeNextIdStr());
@@ -170,11 +173,14 @@ public class AppearancePatentTaskService {
throw new BusinessException("序列化解析结果失败");
}
fileTaskMapper.insert(task);
long taskInsertedAt = System.nanoTime();
String aggregateScopeKey = buildAggregateScopeKey(sourceFiles);
String sourceScopeHash = DigestUtil.sha256Hex(aggregateScopeKey);
String parsedPayload = buildParsedPayloadJson(request.getAiPrompt(), sourceFiles, mergedHeaders, groups, allRows);
long payloadBuiltAt = System.nanoTime();
String parsedPayloadPointer = storeParsedPayload(task.getId(), sourceScopeHash, parsedPayload);
long payloadStoredAt = System.nanoTime();
task.setResultJson(buildTaskResultJson(request.getAiPrompt(), sourceFiles, parsedPayloadPointer));
task.setUpdatedAt(LocalDateTime.now());
fileTaskMapper.updateById(task);
@@ -203,6 +209,7 @@ public class AppearancePatentTaskService {
scope.setCreatedAt(LocalDateTime.now());
scope.setUpdatedAt(LocalDateTime.now());
taskScopeStateMapper.insert(scope);
long persistedAt = System.nanoTime();
AppearancePatentParseVo vo = new AppearancePatentParseVo();
vo.setTaskId(task.getId());
@@ -215,6 +222,20 @@ public class AppearancePatentTaskService {
vo.setAiPrompt(normalize(request.getAiPrompt()));
vo.setItems(allRows);
vo.setGroups(groups);
long finishedAt = System.nanoTime();
log.info("[appearance-patent] parse timing taskId={} files={} rows={} groups={} totalMs={} parseMs={} groupMs={} taskInsertMs={} payloadJsonMs={} payloadStoreMs={} persistMs={} responseMs={}",
task.getId(),
sourceFiles.size(),
allRows.size(),
groups.size(),
elapsedMs(startedAt, finishedAt),
elapsedMs(parseStartedAt, parsedAt),
elapsedMs(parsedAt, groupedAt),
elapsedMs(groupedAt, taskInsertedAt),
elapsedMs(taskInsertedAt, payloadBuiltAt),
elapsedMs(payloadBuiltAt, payloadStoredAt),
elapsedMs(payloadStoredAt, persistedAt),
elapsedMs(persistedAt, finishedAt));
return vo;
}
@@ -1615,9 +1636,9 @@ public class AppearancePatentTaskService {
payload.setAiPrompt(normalize(aiPrompt));
payload.setSourceFiles(sourceFiles == null ? List.of() : sourceFiles);
payload.setHeaders(headers == null ? List.of() : headers);
payload.setItems(allRows == null ? List.of() : allRows);
payload.setItems(List.of());
payload.setGroups(groups == null ? List.of() : groups);
payload.setAllItems(allRows == null ? List.of() : allRows);
payload.setAllItems(List.of());
return writeJson(payload, "保存解析结果失败");
}
@@ -1633,7 +1654,11 @@ public class AppearancePatentTaskService {
}
private String storeParsedPayload(Long taskId, String scopeHash, String parsedPayloadJson) {
return transientPayloadStorageService.storeParsedPayload(MODULE_TYPE, taskId, scopeHash, parsedPayloadJson, true);
return transientPayloadStorageService.storeParsedPayloadFast(MODULE_TYPE, taskId, scopeHash, parsedPayloadJson, true);
}
private long elapsedMs(long start, long end) {
return (end - start) / 1_000_000L;
}
private AppearancePatentParsedPayloadDto readParsedPayload(FileTaskEntity task) {
@@ -1642,15 +1667,41 @@ public class AppearancePatentTaskService {
String pointer = root.path("parsedPayloadRef").asText("");
if (!pointer.isBlank()) {
String json = transientPayloadStorageService.resolvePayload(pointer, "read appearance patent parsed payload failed");
return objectMapper.readValue(json, AppearancePatentParsedPayloadDto.class);
return hydrateParsedPayloadRows(objectMapper.readValue(json, AppearancePatentParsedPayloadDto.class));
}
if (root.path("allItems").isArray()) {
return objectMapper.treeToValue(root, AppearancePatentParsedPayloadDto.class);
return hydrateParsedPayloadRows(objectMapper.treeToValue(root, AppearancePatentParsedPayloadDto.class));
}
} catch (Exception ex) {
throw new BusinessException("读取解析载荷失败");
}
return new AppearancePatentParsedPayloadDto();
return hydrateParsedPayloadRows(new AppearancePatentParsedPayloadDto());
}
private AppearancePatentParsedPayloadDto hydrateParsedPayloadRows(AppearancePatentParsedPayloadDto payload) {
if (payload == null) {
return new AppearancePatentParsedPayloadDto();
}
List<AppearancePatentParsedRowVo> rows = payload.getAllItems();
if (rows == null || rows.isEmpty()) {
rows = payload.getItems();
}
if ((rows == null || rows.isEmpty()) && payload.getGroups() != null) {
rows = payload.getGroups().stream()
.filter(Objects::nonNull)
.flatMap(group -> group.getItems() == null ? java.util.stream.Stream.empty() : group.getItems().stream())
.toList();
}
if (rows == null) {
rows = List.of();
}
if (payload.getAllItems() == null || payload.getAllItems().isEmpty()) {
payload.setAllItems(rows);
}
if (payload.getItems() == null || payload.getItems().isEmpty()) {
payload.setItems(rows);
}
return payload;
}
private Map<String, AppearancePatentResultRowDto> readChunkRows(TaskChunkEntity chunk) {