diff --git a/src/main/java/com/gxwebsoft/ai/dto/AuditContentRequest.java b/src/main/java/com/gxwebsoft/ai/dto/AuditContentRequest.java index 9cb5fb1..ef95bac 100644 --- a/src/main/java/com/gxwebsoft/ai/dto/AuditContentRequest.java +++ b/src/main/java/com/gxwebsoft/ai/dto/AuditContentRequest.java @@ -35,6 +35,11 @@ public class AuditContentRequest { */ private String history; + /** + * 知识库关键词 + */ + private String keywords; + /** * 优化建议 */ diff --git a/src/main/java/com/gxwebsoft/ai/service/impl/AbstractAuditContentService.java b/src/main/java/com/gxwebsoft/ai/service/impl/AbstractAuditContentService.java index caf8dcc..b70f13e 100644 --- a/src/main/java/com/gxwebsoft/ai/service/impl/AbstractAuditContentService.java +++ b/src/main/java/com/gxwebsoft/ai/service/impl/AbstractAuditContentService.java @@ -25,6 +25,8 @@ import org.springframework.beans.factory.annotation.Autowired; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; @Slf4j @@ -50,7 +52,7 @@ public abstract class AbstractAuditContentService { protected JSONArray callWorkflow(String url, String token, JSONObject requestBody, String workflowName) { try { log.info("调用{}工作流,请求体长度: {}", workflowName, requestBody.toString().length()); - + String result = HttpUtil.createPost(url) .header("Authorization", token) .header("Content-Type", "application/json") @@ -58,34 +60,95 @@ public abstract class AbstractAuditContentService { .timeout(10 * 60 * 1000) .execute() .body(); - + log.info("{}工作流返回结果长度: {}", workflowName, result.length()); - + ObjectMapper objectMapper = new ObjectMapper(); JsonNode rootNode = objectMapper.readTree(result); - + String outputText = rootNode.path("data") - .path("outputs") - .path("result") - .asText(); - + .path("outputs") + .path("result") + .asText(); + if (StrUtil.isBlank(outputText)) { - log.warn("{}工作流返回结果为空", workflowName); + log.warn("{}工作流返回 result 为空", workflowName); return new JSONArray(); } - - JsonNode arrayNode = objectMapper.readTree(outputText); - JSONArray jsonArray = JSONArray.parseArray(arrayNode.toString()); - + + // ★ NEW:先从 ```json``` 中提取 + String jsonText = extractJsonFromMarkdown(outputText); + + // ★ NEW:兜底提取 + if (StrUtil.isBlank(jsonText)) { + jsonText = extractFirstJson(outputText); + } + + if (StrUtil.isBlank(jsonText)) { + log.error("{} 工作流返回内容无法解析为 JSON,原始内容:{}", workflowName, outputText); + throw new RuntimeException("Dify 返回内容中未找到有效 JSON"); + } + + JSONArray jsonArray = JSONArray.parseArray(jsonText); + log.info("成功解析{}工作流返回数据,记录数: {}", workflowName, jsonArray.size()); return jsonArray; - + } catch (Exception e) { log.error("调用{}工作流失败", workflowName, e); throw new RuntimeException("调用" + workflowName + "工作流失败: " + e.getMessage(), e); } } + /** + * 从 ```json 代码块中提取 JSON + */ + private static String extractJsonFromMarkdown(String text) { + if (StrUtil.isBlank(text)) { + return null; + } + Pattern pattern = Pattern.compile("```json\\s*(.*?)\\s*```", Pattern.DOTALL); + Matcher matcher = pattern.matcher(text); + if (matcher.find()) { + return matcher.group(1).trim(); + } + return null; + } + + /** + * 兜底:从混合文本中提取第一个完整 JSON({} 或 []) + */ + private static String extractFirstJson(String text) { + if (StrUtil.isBlank(text)) return null; + + int start = -1; + char open = 0, close = 0; + + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + if (c == '{' || c == '[') { + start = i; + open = c; + close = (c == '{') ? '}' : ']'; + break; + } + } + if (start == -1) return null; + + int count = 0; + for (int i = start; i < text.length(); i++) { + char c = text.charAt(i); + if (c == open) count++; + if (c == close) count--; + if (count == 0) { + return text.substring(start, i + 1); + } + } + return null; + } + + + /** * 构建工作流请求通用方法 */ @@ -208,11 +271,22 @@ public abstract class AbstractAuditContentService { String docName = extractDocumentName(node); String docId = extractDocumentId(node); String fileUrl = fileUrlMap.get(docId); + if(StrUtil.isBlank(fileUrl)) { + fileUrl = extractDocumentUrl(node); + } + if(StrUtil.isBlank(fileUrl)) { + fileUrl = "无链接"; + } // 处理文件URL为空的情况 - String url = StrUtil.isNotBlank(fileUrl) ? fileUrl : "无"; +// String url = StrUtil.isNotBlank(fileUrl) ? fileUrl : "无"; // 格式化结果 - String formattedText = String.format("《%s》【FileUrl:%s】%s", docName, url, text); +// String formattedText = String.format("《%s》【FileUrl:%s】%s", docName, url, text); + JSONObject json = new JSONObject(); + json.put("document_name", docName); + json.put("file_url", fileUrl); + json.put("content", text); + String formattedText = json.toJSONString(); results.add(formattedText); } catch (Exception e) { log.warn("处理知识库节点失败", e); @@ -259,6 +333,25 @@ public abstract class AbstractAuditContentService { return "相关文档"; } + /** + * 提取文档url通用方法 + */ + protected String extractDocumentUrl(RetrieveResponseBodyDataNodes node) { + try { + Object metadataObj = node.getMetadata(); + if (metadataObj instanceof Map) { + Map metadata = (Map) metadataObj; + Object docIdObj = metadata.get("doc_url"); + if (docIdObj != null) { + return docIdObj.toString(); + } + } + } catch (Exception e) { + log.debug("提取文档名称失败", e); + } + return "相关文档"; + } + /** * 构建成功响应通用方法 */