提交更新
This commit is contained in:
+74
-2
@@ -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 "";
|
||||
|
||||
+58
-7
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user