fix: AI服务改用HttpsURLConnection解决SSL兼容问题+超时5分钟+超时直接提示失败

This commit is contained in:
xuan-bot committed 2026-05-07 08:02:48 +08:00
1 parent 325c22fd09
commit a0ac75ebca
2 files changed
+80 -48

No files matched your search

+57 -35
View File
@@ -9,11 +9,15 @@ import xuan.config.AiProperties;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.cert.X509Certificate;
@@ -33,27 +37,11 @@ public class AiService {
}
private OkHttpClient buildClient() {
try {
TrustManager[] trustManagers = new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagers, null);
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
return new OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManagers[0])
.hostnameVerifier((hostname, session) -> true)
.connectTimeout(properties.getConnectTimeout(), TimeUnit.SECONDS)
.readTimeout(properties.getReadTimeout(), TimeUnit.SECONDS)
.writeTimeout(properties.getConnectTimeout(), TimeUnit.SECONDS)
.build();
} catch (Exception e) {
throw new RuntimeException("Failed to build OkHttpClient", e);
}
return new OkHttpClient.Builder()
.connectTimeout(properties.getConnectTimeout(), TimeUnit.SECONDS)
.readTimeout(properties.getReadTimeout(), TimeUnit.SECONDS)
.writeTimeout(properties.getConnectTimeout(), TimeUnit.SECONDS)
.build();
}
public String chat(String systemPrompt, String userMessage) {
@@ -72,7 +60,7 @@ public class AiService {
try {
String baseUrl = properties.getBaseUrl().replaceAll("/+$", "");
String url = baseUrl + "/v1/chat/completions";
String urlStr = baseUrl + "/v1/chat/completions";
JSONObject requestBody = new JSONObject();
requestBody.put("model", properties.getModel());
@@ -95,20 +83,47 @@ public class AiService {
String payload = requestBody.toJSONString();
Request request = new Request.Builder()
.url(url)
.post(RequestBody.create(payload, MediaType.parse("application/json; charset=utf-8")))
.addHeader("Authorization", "Bearer " + apiKey)
.addHeader("Content-Type", "application/json; charset=utf-8")
.build();
URL url = new URL(urlStr);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setConnectTimeout(properties.getConnectTimeout() * 1000);
conn.setReadTimeout(properties.getReadTimeout() * 1000);
conn.setRequestProperty("Authorization", "Bearer " + apiKey);
conn.setRequestProperty("Content-Type", "application/json; charset=utf-8");
try (Response response = client.newCall(request).execute()) {
String responseBody = response.body() != null ? response.body().string() : "";
if (!response.isSuccessful()) {
return "AI服务请求失败(HTTP " + response.code() + "):" + responseBody;
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
}
return parseOpenAIResponse(responseBody);
}, null);
conn.setSSLSocketFactory(sslContext.getSocketFactory());
conn.setHostnameVerifier((hostname, session) -> true);
try (OutputStream os = conn.getOutputStream()) {
os.write(payload.getBytes(StandardCharsets.UTF_8));
}
int responseCode = conn.getResponseCode();
StringBuilder responseBody = new StringBuilder();
try (BufferedReader br = new BufferedReader(
new InputStreamReader(
responseCode >= 200 && responseCode < 300
? conn.getInputStream() : conn.getErrorStream(),
StandardCharsets.UTF_8))) {
String line;
while ((line = br.readLine()) != null) {
responseBody.append(line);
}
}
if (responseCode < 200 || responseCode >= 300) {
return "AI服务请求失败(HTTP " + responseCode + "):" + responseBody;
}
return parseOpenAIResponse(responseBody.toString());
} catch (Exception e) {
e.printStackTrace();
return "AI分析暂时不可用,请稍后再试。错误信息:" + e.getMessage();
@@ -123,7 +138,14 @@ public class AiService {
JSONObject choice = choices.getJSONObject(0);
JSONObject message = choice.getJSONObject("message");
if (message != null) {
return message.getString("content");
String content = message.getString("content");
if (content != null && content.startsWith("<think")) {
int endIdx = content.indexOf("</think");
if (endIdx != -1) {
content = content.substring(endIdx + 8).trim();
}
}
return content;
}
}
return "AI未返回有效内容";
+23 -13
View File
@@ -10,6 +10,13 @@ const request = (options, retryCount = 0) => {
})
}
let slowTimer = setTimeout(() => {
uni.showLoading({
title: '测算时间有点长,请耐心等待...',
mask: true
})
}, 30000)
uni.request({
url: BASE_URL + options.url,
method: options.method || 'GET',
@@ -18,12 +25,11 @@ const request = (options, retryCount = 0) => {
'Content-Type': 'application/json',
...options.header
},
timeout: 60000,
timeout: 300000,
success: (res) => {
clearTimeout(slowTimer)
uni.hideLoading()
if (res.statusCode === 200) {
if (retryCount > 0) {
uni.hideLoading()
}
resolve(res.data)
} else if (res.statusCode >= 500 && retryCount < MAX_RETRY) {
retryCount++
@@ -35,25 +41,31 @@ const request = (options, retryCount = 0) => {
request(options, retryCount).then(resolve).catch(reject)
}, 2000)
} else {
if (retryCount > 0) {
uni.hideLoading()
}
reject(new Error('网络请求失败: ' + res.statusCode))
}
},
fail: (err) => {
clearTimeout(slowTimer)
uni.hideLoading()
const isTimeout = err.errMsg && (err.errMsg.indexOf('timeout') !== -1 || err.errMsg.indexOf('超时') !== -1)
if (isTimeout && retryCount < MAX_RETRY) {
if (isTimeout) {
uni.showModal({
title: '测算失败',
content: 'AI分析超时,请稍后再试',
showCancel: false,
confirmText: '知道了'
})
reject(new Error('测算超时'))
} else if (retryCount < MAX_RETRY) {
retryCount++
uni.showLoading({
title: '请求超时,重试中(' + retryCount + '/' + MAX_RETRY + ')...',
title: '重新连接中(' + retryCount + '/' + MAX_RETRY + ')...',
mask: true
})
setTimeout(() => {
request(options, retryCount).then(resolve).catch(reject)
}, 2000)
} else if (retryCount >= MAX_RETRY) {
uni.hideLoading()
} else {
uni.showModal({
title: '请求失败',
content: '已重试' + MAX_RETRY + '次仍无法连接服务器,请检查网络后重试',
@@ -61,8 +73,6 @@ const request = (options, retryCount = 0) => {
confirmText: '知道了'
})
reject(new Error('重试' + MAX_RETRY + '次后仍失败:' + (err.errMsg || '网络连接失败')))
} else {
reject(new Error(err.errMsg || '网络连接失败'))
}
}
})