|
|
|
@ -1,16 +1,19 @@
|
|
|
|
|
package com.dsideal.Res.Test;
|
|
|
|
|
|
|
|
|
|
import cn.hutool.core.io.FileUtil;
|
|
|
|
|
import cn.hutool.http.HttpRequest;
|
|
|
|
|
import cn.hutool.http.HttpResponse;
|
|
|
|
|
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 java.io.BufferedReader;
|
|
|
|
|
import org.apache.commons.codec.digest.DigestUtils;
|
|
|
|
|
import java.io.File;
|
|
|
|
|
import java.io.InputStreamReader;
|
|
|
|
|
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
|
|
|
|
@ -20,101 +23,131 @@ public class CallDeepSeek {
|
|
|
|
|
new Thread(() -> {
|
|
|
|
|
StringBuilder fullResponse = new StringBuilder();
|
|
|
|
|
try {
|
|
|
|
|
// 修改提示词确保只返回HTML
|
|
|
|
|
String htmlPrompt = prompt + "请严格按照以下HTML模板格式输出SQL血缘关系图,只返回HTML代码:\n" +
|
|
|
|
|
"<!DOCTYPE html>\n" +
|
|
|
|
|
"<html>\n" +
|
|
|
|
|
"<head>\n" +
|
|
|
|
|
" <title>SQL血缘关系图</title>\n" +
|
|
|
|
|
" <script src='https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js'></script>\n" +
|
|
|
|
|
" <style>\n" +
|
|
|
|
|
" .mermaid { margin: 20px auto; }\n" +
|
|
|
|
|
" body { font-family: Arial; text-align: center; }\n" +
|
|
|
|
|
" </style>\n" +
|
|
|
|
|
"</head>\n" +
|
|
|
|
|
"<body>\n" +
|
|
|
|
|
" <h2>SQL血缘关系图</h2>\n" +
|
|
|
|
|
" <div class='mermaid'>\n" +
|
|
|
|
|
" [请在此处插入mermaid格式的血缘关系图]\n" +
|
|
|
|
|
" </div>\n" +
|
|
|
|
|
" <script>mermaid.initialize({startOnLoad:true});</script>\n" +
|
|
|
|
|
"</body>\n" +
|
|
|
|
|
"</html>";
|
|
|
|
|
|
|
|
|
|
JSONObject json = new JSONObject();
|
|
|
|
|
json.set("model", "deepseek-chat");
|
|
|
|
|
// 修改为对象形式构建messages
|
|
|
|
|
// 修改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" +
|
|
|
|
|
"<!DOCTYPE html>\n" +
|
|
|
|
|
"<html>\n" +
|
|
|
|
|
"<head>\n" +
|
|
|
|
|
" <title>SQL血缘关系图</title>\n" +
|
|
|
|
|
" <script src='https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js'></script>\n" +
|
|
|
|
|
" <style>\n" +
|
|
|
|
|
" .mermaid { margin: 20px auto; }\n" +
|
|
|
|
|
" body { font-family: Arial; text-align: center; }\n" +
|
|
|
|
|
" </style>\n" +
|
|
|
|
|
"</head>\n" +
|
|
|
|
|
"<body>\n" +
|
|
|
|
|
" <h2>SQL血缘关系图</h2>\n" +
|
|
|
|
|
" <div class='mermaid'>\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" +
|
|
|
|
|
" </div>\n" +
|
|
|
|
|
" <script>mermaid.initialize({startOnLoad:true, theme: 'default'});</script>\n" +
|
|
|
|
|
"</body>\n" +
|
|
|
|
|
"</html>\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);
|
|
|
|
|
json.set("messages", messages);
|
|
|
|
|
|
|
|
|
|
json.set("stream", true);
|
|
|
|
|
System.out.println(json); // 打印最终请求体
|
|
|
|
|
HttpRequest request = HttpRequest.post(API_URL)
|
|
|
|
|
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")
|
|
|
|
|
.body(json.toString());
|
|
|
|
|
|
|
|
|
|
HttpResponse response = request.execute();
|
|
|
|
|
if (response.isOk()) {
|
|
|
|
|
try (BufferedReader reader = new BufferedReader(
|
|
|
|
|
new InputStreamReader(response.bodyStream(), "UTF-8"))) {
|
|
|
|
|
|
|
|
|
|
String line;
|
|
|
|
|
while ((line = reader.readLine()) != null) {
|
|
|
|
|
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);
|
|
|
|
|
// 实时输出到控制台
|
|
|
|
|
System.out.print(content);
|
|
|
|
|
System.out.flush();
|
|
|
|
|
listener.onData(content);
|
|
|
|
|
.header("Accept", "text/event-stream") // 确保Accept头正确
|
|
|
|
|
.POST(java.net.http.HttpRequest.BodyPublishers.ofString(jsonPayload.toString(), StandardCharsets.UTF_8))
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
// 使用 BodyHandlers.ofLines() 来处理流式响应
|
|
|
|
|
CompletableFuture<Void> 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());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
System.err.println("解析SSE数据错误: " + e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
// 流结束后的处理
|
|
|
|
|
String htmlContent = fullResponse.toString();
|
|
|
|
|
if (htmlContent.contains("<!DOCTYPE html>") && htmlContent.contains("</html>")) {
|
|
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 保存HTML到文件
|
|
|
|
|
String htmlContent = fullResponse.toString();
|
|
|
|
|
if (htmlContent.contains("<!DOCTYPE html>") && htmlContent.contains("</html>")) {
|
|
|
|
|
FileUtil.writeString(htmlContent, new File("C:\\1.html"), "UTF-8");
|
|
|
|
|
listener.onComplete("HTML已成功保存到C:\\1.html");
|
|
|
|
|
} else {
|
|
|
|
|
listener.onError("返回内容不是有效的HTML");
|
|
|
|
|
}
|
|
|
|
|
}).exceptionally(e -> {
|
|
|
|
|
listener.onError("请求或处理异常: " + e.getMessage());
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
return null;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
future.join(); // 等待异步操作完成,对于main线程的简单示例是必要的
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
listener.onError(e.getMessage());
|
|
|
|
|
listener.onError("发生意外错误: " + e.getMessage());
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}).start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
String sql = FileUtil.readUtf8String(new File(PathKit.getRootClassPath()+"/XueYuan.sql"));
|
|
|
|
|
String prompt = "你是一个数据库SQL的血缘关系分析专家,我将提供SQL语句给你,帮我整理数据血缘关系,并且绘制HTML页面,以展示最终的可视化形式展现。";
|
|
|
|
|
|
|
|
|
|
callDeepSeekStream(prompt + "\n" + sql, new SSEListener() {
|
|
|
|
|
|
|
|
|
|
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.print(data); // 实时打印
|
|
|
|
|
System.out.flush(); // 确保立即刷新到控制台
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@ -128,11 +161,10 @@ public class CallDeepSeek {
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
Thread.sleep(30000);
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
// 移除 Thread.sleep(30000);
|
|
|
|
|
// 如果主线程需要等待SSE完成,可以考虑使用更优雅的同步机制,
|
|
|
|
|
// 例如 CountDownLatch,或者让 callDeepSeekStream 方法返回一个 Future。
|
|
|
|
|
// 但对于简单的控制台流式输出演示,移除休眠即可观察到流式效果。
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public interface SSEListener {
|
|
|
|
|