package com.dsideal.Res.Test; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.CharsetUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSONArray; import com.jfinal.kit.PathKit; import org.apache.commons.codec.digest.DigestUtils; import java.io.File; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.util.concurrent.CompletableFuture; public class CallDeepSeek { private static final String API_KEY = "sk-44ae895eeb614aa1a9c6460579e322f1"; // 请替换为您的API KEY private static final String API_URL = "https://api.deepseek.com/v1/chat/completions"; public static void callDeepSeekStream(String prompt, SSEListener listener) { new Thread(() -> { StringBuilder fullResponse = new StringBuilder(); try { // 修改htmlPrompt变量,在提示词中明确要求输出格式 String htmlPrompt = prompt + "请严格按照以下mermaid配置输出SQL血缘关系图:\n" + "1. 使用以下固定样式配置:\n" + " graph TD\n" + " classDef default fill:#f9f9f9,stroke:#333,stroke-width:1px\n" + " classDef input fill:#D6EAF8,stroke:#333\n" + " classDef output fill:#D5F5E3,stroke:#333\n" + " linkStyle default stroke:#333,stroke-width:2px\n" + "2. 按照以下HTML模板格式输出,不要生成html以外的其它文字:\n" + "\n" + "\n" + "\n" + " SQL血缘关系图\n" + " \n" + " \n" + "\n" + "\n" + "

SQL血缘关系图

\n" + "
\n" + " graph TD\n" + " classDef default fill:#f9f9f9,stroke:#333,stroke-width:1px\n" + " classDef input fill:#D6EAF8,stroke:#333\n" + " classDef output fill:#D5F5E3,stroke:#333\n" + " linkStyle default stroke:#333,stroke-width:2px\n" + " [请在此处插入mermaid格式的血缘关系图]\n" + "
\n" + " \n" + "\n" + "\n"; JSONObject jsonPayload = new JSONObject(); jsonPayload.set("model", "deepseek-chat"); JSONObject message = new JSONObject(); message.set("role", "user"); message.set("content", htmlPrompt); JSONArray messages = new JSONArray(); messages.add(message); jsonPayload.set("messages", messages); jsonPayload.set("stream", true); HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_1_1) // 明确指定HTTP版本 .build(); java.net.http.HttpRequest request = java.net.http.HttpRequest.newBuilder() .uri(URI.create(API_URL)) .header("Content-Type", "application/json") .header("Authorization", "Bearer " + API_KEY) .header("Accept", "text/event-stream") // 确保Accept头正确 .POST(java.net.http.HttpRequest.BodyPublishers.ofString(jsonPayload.toString(), StandardCharsets.UTF_8)) .build(); // 使用 BodyHandlers.ofLines() 来处理流式响应 CompletableFuture future = client.sendAsync(request, HttpResponse.BodyHandlers.ofLines()) .thenAccept(response -> { if (response.statusCode() == 200) { response.body().forEach(line -> { if (line.startsWith("data:")) { String data = line.substring(5).trim(); if (!data.equals("[DONE]")) { try { JSONObject jsonData = JSONUtil.parseObj(data); if (jsonData.containsKey("choices")) { String content = jsonData.getJSONArray("choices") .getJSONObject(0) .getJSONObject("delta") .getStr("content", ""); if (content != null && !content.isEmpty()) { fullResponse.append(content); listener.onData(content); // 回调监听器 } } } catch (Exception e) { System.err.println("解析SSE JSON数据错误: " + data + " \nError: " + e.getMessage()); } } } }); // 流结束后的处理 String htmlContent = fullResponse.toString(); if (htmlContent.contains("") && htmlContent.contains("")) { FileUtil.writeString(htmlContent, new File("C:\\1.html"), "UTF-8"); listener.onComplete("HTML已成功保存到C:\\1.html"); } else { listener.onError("最终返回内容不是有效的HTML: " + htmlContent.substring(0, Math.min(htmlContent.length(), 200)) + "..."); } } else { listener.onError("API请求失败: " + response.statusCode() + " Body: " + response.body().toString()); } }).exceptionally(e -> { listener.onError("请求或处理异常: " + e.getMessage()); e.printStackTrace(); return null; }); future.join(); // 等待异步操作完成,对于main线程的简单示例是必要的 } catch (Exception e) { listener.onError("发生意外错误: " + e.getMessage()); e.printStackTrace(); } }).start(); } public static void main(String[] args) { String sqlContent = FileUtil.readString("XueYuan.sql", CharsetUtil.CHARSET_UTF_8); String md5 = DigestUtils.md5Hex(sqlContent); String outputPath = "C:\\" + md5 + ".html"; File outputFile = new File(outputPath); if (outputFile.exists()) { System.out.println("文件已存在,跳过生成:" + outputPath); return; } String prompt = "你是一个数据库SQL的血缘关系分析专家,我将提供SQL语句给你,帮我整理数据血缘关系,并且绘制HTML页面,以展示最终的可视化展现。"; callDeepSeekStream(prompt + "\n" + sqlContent, new SSEListener() { @Override public void onData(String data) { System.out.print(data); // 实时打印 System.out.flush(); // 确保立即刷新到控制台 } @Override public void onComplete(String fullResponse) { System.out.println("\n\n完整回复: " + fullResponse); } @Override public void onError(String error) { System.err.println("错误: " + error); } }); // 移除 Thread.sleep(30000); // 如果主线程需要等待SSE完成,可以考虑使用更优雅的同步机制, // 例如 CountDownLatch,或者让 callDeepSeekStream 方法返回一个 Future。 // 但对于简单的控制台流式输出演示,移除休眠即可观察到流式效果。 } public interface SSEListener { void onData(String data); void onComplete(String fullResponse); void onError(String error); } }